still have some bug about select file

This commit is contained in:
Kenta420 2023-10-20 17:51:07 +07:00
commit ad32fe38ea
11 changed files with 325 additions and 130 deletions

View file

@ -93,13 +93,13 @@ const routes: Routes = [
).then((m) => m.RecipeDetailsComponent), ).then((m) => m.RecipeDetailsComponent),
canActivate: [authGuard], canActivate: [authGuard],
}, },
{ // {
path: 'log', // path: 'log',
loadComponent: () => // loadComponent: () =>
import('./features/changelog/changelog.component').then( // import('./features/changelog/changelog.component').then(
(m) => m.ChangelogComponent // (m) => m.ChangelogComponent
), // ),
}, // },
{ {
path: '**', path: '**',
redirectTo: 'recipes', redirectTo: 'recipes',

View file

@ -106,4 +106,20 @@ export class RecipeService {
getRecipeFileNames(country: string): string[] { getRecipeFileNames(country: string): string[] {
return this.recipeFiles[country]; return this.recipeFiles[country];
} }
editChanges(version: string, change: any){
console.log("target version = ", version);
console.log("change in edit: ",change.value)
return this._httpClient.post<{
status: string
}>(
environment.api + ('/recipes/edit/'+version),
change.value,
{ withCredentials: true, responseType: 'json', }
).subscribe({
next(value) {
console.log(value, change.value)
},
});
}
} }

View file

@ -94,4 +94,16 @@
<!-- Temporary remove merge tool --> <!-- Temporary remove merge tool -->
<div class="card h-full max-h-full">
<h2 class="card-title p-2">Diff</h2>
<!-- selectable version -->
<div class="card-body bg-stone-200">
<h3 class="card-title">Select Version</h3>
<div class="rela flex-row m-2" id="diff_version_select">
<input class="input input-md input-bordered m-2 bg-red-100" type="number" name="master_target" placeholder="Master Version" id="master_target">
<input class="input input-md input-bordered m-2 bg-green-100" type="number" name="target" placeholder="Target Version" id="target">
</div>
</div>
<button type="button" class="button bg-stone-400 p-2 rounded m-2 text-black font-bold" id="diff_load" (click)="addVersion()">&#43; Add Version</button>
</div>
</div> </div>

View file

@ -150,5 +150,12 @@ export class ChangelogComponent {
} }
// Add version button
addVersion(){
let dvs = document.getElementById("diff_version_select");
dvs!.innerHTML += `<input class="input input-md input-bordered m-2 bg-green-100" type="number" name="target" placeholder="Target Version" id="target">`;
}
} }

View file

@ -186,6 +186,14 @@ export class RecipeDetailsComponent implements OnInit {
message: 'Do you want to save changes?', message: 'Do you want to save changes?',
confirmCallBack: () => { confirmCallBack: () => {
console.log('confirm save'); console.log('confirm save');
// TODO: update value in targeted recipe
this._recipeService.editChanges(
this._recipeService.getCurrentVersion(),
{
...this.recipeDetail
}
);
console.log("Sending changes")
this._router.navigate(['/recipes']); this._router.navigate(['/recipes']);
}, },
}; };

View file

@ -9,7 +9,7 @@
<div class="flex flex-col"> <div class="flex flex-col">
<span <span
>Recipe Version {{ recipes?.MachineSetting?.configNumber }} | >Recipe Version {{ recipes?.MachineSetting?.configNumber }} |
{{ fileName }}</span {{ currentFile }}</span
> >
</div> </div>
<div class="flex flex-col ml-5"> <div class="flex flex-col ml-5">
@ -23,16 +23,17 @@
<div <div
class="modal-box max-w-[600px] overflow-visible flex flex-col justify-center items-center gap-5" class="modal-box max-w-[600px] overflow-visible flex flex-col justify-center items-center gap-5"
> >
<h3 class="font-bold text-lg">เลือก Recipe ไฟล์</h3> <h3 class="font-bold text-lg">Select Recipe File</h3>
<div class="flex flex-row gap-5"> <div class="flex flex-row gap-5">
<div class="dropdown dropdown-end"> <div class="dropdown dropdown-end">
<input <input
type="text" type="text"
tabindex="0" tabindex="0"
placeholder="เลือก Recipe File" placeholder="Select Country"
class="input input-bordered input-sm w-full max-w-xs" class="input input-bordered input-sm w-full max-w-xs"
(input)="setRecipeVersion($event)" [value]="currentCountryFilter.getValue()"
(focus)="getRecipeVersions()" (input)="setCountryFilter($event)"
(focus)="getRecipeCountries()"
/> />
<div <div
class="dropdown-content z-[1000] min-w-[200px] max-h-[500px] overflow-y-auto" class="dropdown-content z-[1000] min-w-[200px] max-h-[500px] overflow-y-auto"
@ -41,34 +42,32 @@
tabindex="0" tabindex="0"
class="menu p-2 shadow bg-base-100 rounded-box w-auto" class="menu p-2 shadow bg-base-100 rounded-box w-auto"
> >
<li *ngFor="let recipeVersion of recipeVersions"> <li *ngFor="let country of recipeCountryFiltered">
<a (click)="loadRecipe(recipeVersion)">{{ <a (click)="countrySelected(country)">{{
recipeVersion country
}}</a> }}</a>
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
<div class="dropdown dropdown-end"> <div *ngIf="isCountrySelected" class="dropdown dropdown-end">
<input <input
type="text" type="text"
tabindex="0" tabindex="1"
placeholder="เลือก Recipe File" placeholder="เลือก Recipe File"
class="input input-bordered input-sm w-full max-w-xs" class="input input-bordered input-sm w-full max-w-xs"
(input)="setRecipeVersion($event)" (input)="setFileFilter($event)"
(focus)="getRecipeVersions()" (focus)="getRecipeFileCountries()"
/> />
<div <div
class="dropdown-content z-[1000] min-w-[200px] max-h-[500px] overflow-y-auto" class="dropdown-content z-[1000] min-w-[200px] max-h-[500px] overflow-y-auto"
> >
<ul <ul
tabindex="0" tabindex="1"
class="menu p-2 shadow bg-base-100 rounded-box w-auto" class="menu p-2 shadow bg-base-100 rounded-box w-auto"
> >
<li *ngFor="let recipeVersion of recipeVersions"> <li *ngFor="let file of recipeFileCountries">
<a (click)="loadRecipe(recipeVersion)">{{ <a (click)="loadRecipe(file)">{{ file }}</a>
recipeVersion
}}</a>
</li> </li>
</ul> </ul>
</div> </div>
@ -257,4 +256,11 @@
</div> </div>
</div> </div>
</div> </div>
<button
class="btn btn-circle fixed z-100 bottom-5 right-1"
(click)="scrollToTop()"
>
^
</button>
</main> </main>

View file

@ -1,4 +1,10 @@
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; import {
Component,
ElementRef,
OnDestroy,
OnInit,
ViewChild,
} from '@angular/core';
import { UserService } from 'src/app/core/services/user.service'; import { UserService } from 'src/app/core/services/user.service';
import { User } from 'src/app/core/models/user.model'; import { User } from 'src/app/core/models/user.model';
import { DatePipe, NgFor, NgIf } from '@angular/common'; import { DatePipe, NgFor, NgIf } from '@angular/common';
@ -6,27 +12,20 @@ import { Recipe, Recipe01 } from 'src/app/core/models/recipe.model';
import { RecipeService } from 'src/app/core/services/recipe.service'; import { RecipeService } from 'src/app/core/services/recipe.service';
import { environment } from 'src/environments/environment'; import { environment } from 'src/environments/environment';
import { RecipeModalComponent } from 'src/app/shared/modal/recipe-details/recipe-modal.component'; import { RecipeModalComponent } from 'src/app/shared/modal/recipe-details/recipe-modal.component';
import { BehaviorSubject } from 'rxjs'; import { BehaviorSubject, Subject, Subscriber, Subscription } from 'rxjs';
import * as lodash from 'lodash'; import * as lodash from 'lodash';
import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { ActivatedRoute, Router, RouterLink } from '@angular/router';
interface RecipeFileFilter {
country: string | null;
fileName: string | null;
}
@Component({ @Component({
selector: 'app-recipes', selector: 'app-recipes',
standalone: true, standalone: true,
imports: [RouterLink, NgIf, NgFor, DatePipe, RecipeModalComponent], imports: [RouterLink, NgIf, NgFor, DatePipe, RecipeModalComponent],
templateUrl: './recipes.component.html', templateUrl: './recipes.component.html',
}) })
export class DashboardComponent implements OnInit { export class DashboardComponent implements OnInit, OnDestroy {
recipes: Recipe | null = null; recipes: Recipe | null = null;
recipes01: Recipe01[] | null = null; recipes01: Recipe01[] | null = null;
recipeFileCountries: string[] = []; currentFile: string = '';
recipeFileNames: string[] = [];
fileName: string = '';
tableHeads: string[] = [ tableHeads: string[] = [
'Product Code', 'Product Code',
@ -38,13 +37,6 @@ export class DashboardComponent implements OnInit {
private offset = 0; private offset = 0;
private take = 20; private take = 20;
recipeFileFilter: BehaviorSubject<RecipeFileFilter> =
new BehaviorSubject<RecipeFileFilter>({
country: null,
fileName: null,
});
recipeFileFilter$ = this.recipeFileFilter.asObservable();
isLoaded: boolean = false; isLoaded: boolean = false;
isLoadMore: boolean = false; isLoadMore: boolean = false;
isHasMore: boolean = true; isHasMore: boolean = true;
@ -52,7 +44,12 @@ export class DashboardComponent implements OnInit {
private searchStr = ''; private searchStr = '';
private oldSearchStr = ''; private oldSearchStr = '';
tableCtx?: ElementRef;
@ViewChild('table', { static: false }) set content(table: ElementRef) { @ViewChild('table', { static: false }) set content(table: ElementRef) {
// expose element ref for other fn
this.tableCtx = table;
table.nativeElement.addEventListener( table.nativeElement.addEventListener(
'scroll', 'scroll',
() => { () => {
@ -82,7 +79,7 @@ export class DashboardComponent implements OnInit {
...recipesWithoutRecipe01, ...recipesWithoutRecipe01,
Recipe01: [], Recipe01: [],
}; };
this.fileName = fileName; this.currentFile = fileName;
this.offset += 10; this.offset += 10;
this.isLoadMore = false; this.isLoadMore = false;
this.isHasMore = hasMore; this.isHasMore = hasMore;
@ -114,23 +111,13 @@ export class DashboardComponent implements OnInit {
...recipesWithoutRecipe01, ...recipesWithoutRecipe01,
Recipe01: [], Recipe01: [],
}; };
this.fileName = fileName; this.currentFile = fileName;
this.offset += 10; this.offset += 10;
this.isLoaded = true; this.isLoaded = true;
this.isHasMore = hasMore; this.isHasMore = hasMore;
}); });
this.recipeFileFilter$.subscribe((version) => { this.initRecipeSelection();
this.recipeFileCountries = lodash.filter(
this._recipeService.getRecipeFileCountries(),
(v) => {
if (version.country) {
return v.includes(version.country);
}
return true;
}
);
});
} }
setSearch(event: Event) { setSearch(event: Event) {
@ -155,14 +142,75 @@ export class DashboardComponent implements OnInit {
...recipesWithoutRecipe01, ...recipesWithoutRecipe01,
Recipe01: [], Recipe01: [],
}; };
this.fileName = fileName; this.currentFile = fileName;
this.offset += 10; this.offset += 10;
this.isLoaded = true; this.isLoaded = true;
this.isHasMore = hasMore; this.isHasMore = hasMore;
}); });
} }
loadRecipe(recipeVersion: string) { // Recipe Version selection
currentCountryFilter: BehaviorSubject<string> = new BehaviorSubject<string>(
''
);
currentFileFilter: BehaviorSubject<string> = new BehaviorSubject<string>('');
recipeCountryFiltered: string[] = [];
recipeFileCountries: string[] = [];
currentCountryFilterSubScription: Subscription | null = null;
currentFileFilterSubScription: Subscription | null = null;
selectedCountry: string | null = null;
isCountrySelected: boolean = false;
initRecipeSelection() {
if (this._recipeService.getRecipeFileCountries().length == 0) {
this._recipeService.getRecipeCountries().subscribe((countries) => {
this.recipeCountryFiltered = countries;
});
}
}
setCountryFilter(event: Event) {
this.currentCountryFilter.next((event.target as HTMLInputElement).value);
}
setFileFilter(event: Event) {
this.currentFileFilter.next((event.target as HTMLInputElement).value);
}
getRecipeCountries() {
this.currentCountryFilterSubScription = this.currentCountryFilter.subscribe(
(c) => {
const countries = this._recipeService.getRecipeFileCountries();
if (countries.length > 0) {
this.recipeFileCountries = lodash.filter(countries, (country) =>
country.includes(c)
);
}
}
);
}
getRecipeFileCountries() {
this.currentFileFilterSubScription = this.currentFileFilter.subscribe(
(c) => {
const countries = this._recipeService.getRecipeFileCountries();
if (countries.length > 0) {
this.recipeCountryFiltered = lodash.filter(countries, (country) =>
country.includes(c)
);
}
}
);
}
countrySelected(country: string) {
this.selectedCountry = country;
this.isCountrySelected = true;
}
loadRecipe(recipeFileName: string) {
// clear all recipes // clear all recipes
this.recipes = null; this.recipes = null;
this.recipes01 = null; this.recipes01 = null;
@ -177,7 +225,7 @@ export class DashboardComponent implements OnInit {
offset: this.offset, offset: this.offset,
take: this.take, take: this.take,
search: this.oldSearchStr, search: this.oldSearchStr,
version: recipeVersion, version: recipeFileName,
}) })
.subscribe(({ recipes, hasMore, fileName }) => { .subscribe(({ recipes, hasMore, fileName }) => {
const { Recipe01, ...recipesWithoutRecipe01 } = recipes; const { Recipe01, ...recipesWithoutRecipe01 } = recipes;
@ -190,24 +238,14 @@ export class DashboardComponent implements OnInit {
...recipesWithoutRecipe01, ...recipesWithoutRecipe01,
Recipe01: [], Recipe01: [],
}; };
this.fileName = fileName; this.currentFile = fileName;
this.offset += 10; this.offset += 10;
this.isLoaded = true; this.isLoaded = true;
this.isHasMore = hasMore; this.isHasMore = hasMore;
}); });
} }
setRecipeVersion(event: Event) { // end of Recipe Version selection
this.recipeVersion.next((event.target as HTMLInputElement).value);
}
getRecipeVersions() {
if (this.recipeVersionData.length > 0) return;
this._recipeService.getRecipeCountries().subscribe((versions) => {
this.recipeVersionData = versions;
this.recipeFileCountries = versions;
});
}
openJsonTab() { openJsonTab() {
window.open( window.open(
@ -216,4 +254,17 @@ export class DashboardComponent implements OnInit {
'_blank' '_blank'
); );
} }
scrollToTop() {
this.tableCtx!.nativeElement.scroll;
}
ngOnDestroy(): void {
if (this.currentCountryFilterSubScription) {
this.currentCountryFilterSubScription.unsubscribe();
}
if (this.currentFileFilterSubScription) {
this.currentFileFilterSubScription.unsubscribe();
}
}
} }

View file

@ -8,7 +8,14 @@ import (
"path/filepath" "path/filepath"
"recipe-manager/helpers" "recipe-manager/helpers"
"recipe-manager/models" "recipe-manager/models"
"recipe-manager/services/logger"
"time" "time"
"go.uber.org/zap"
)
var (
Log = logger.GetInstance()
) )
func readFile(version string) *models.Recipe { func readFile(version string) *models.Recipe {
@ -123,6 +130,27 @@ func (d *Data) GetRecipe01() []models.Recipe01 {
return d.currentRecipe.Recipe01 return d.currentRecipe.Recipe01
} }
func (d *Data) GetRecipe01ByProductCode(code string) models.Recipe01 {
result := make([]models.Recipe01, 0)
for _, v := range d.currentRecipe.Recipe01 {
if v.ProductCode == code {
result = append(result, v)
}
}
return result[0]
}
func (d *Data) SetValuesToRecipe(recipe models.Recipe01) {
for _, v := range d.currentRecipe.Recipe01 {
if v.ProductCode == recipe.ProductCode {
v = recipe
break
}
}
}
func (d *Data) GetMaterialSetting(version string) []models.MaterialSetting { func (d *Data) GetMaterialSetting(version string) []models.MaterialSetting {
result := make([]models.MaterialSetting, 0) result := make([]models.MaterialSetting, 0)
@ -235,3 +263,13 @@ func (d *Data) GetCountryIDByName(countryName string) (string, error) {
} }
return "", fmt.Errorf("country name: %s not found", countryName) return "", fmt.Errorf("country name: %s not found", countryName)
} }
func (d *Data) ExportToJSON() []byte {
b_recipe, err := json.Marshal(d.currentRecipe)
if err != nil {
Log.Error("Error when marshal recipe", zap.Error(err))
return nil
}
return b_recipe
}

View file

@ -1,5 +1,7 @@
package models package models
import "encoding/json"
type Recipe struct { type Recipe struct {
Timestamp string `json:"Timestamp"` Timestamp string `json:"Timestamp"`
MachineSetting MachineSetting `json:"MachineSetting"` MachineSetting MachineSetting `json:"MachineSetting"`
@ -82,6 +84,19 @@ type Recipe01 struct {
Weight_float int `json:"weight_float"` Weight_float int `json:"weight_float"`
} }
func (r *Recipe01) ToMap() map[string]interface{} {
var m map[string]interface{}
recipeRecord, _ := json.Marshal(r)
json.Unmarshal(recipeRecord, &m)
return m
}
func (r *Recipe01) FromMap(m map[string]interface{}) Recipe01 {
recipeRecord, _ := json.Marshal(m)
json.Unmarshal(recipeRecord, &r)
return *r
}
type MatRecipe struct { type MatRecipe struct {
MixOrder int `json:"MixOrder"` MixOrder int `json:"MixOrder"`
FeedParameter int `json:"FeedParameter"` FeedParameter int `json:"FeedParameter"`

View file

@ -3,15 +3,19 @@ package routers
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/fs"
"net/http" "net/http"
"os"
"recipe-manager/data" "recipe-manager/data"
"recipe-manager/models" "recipe-manager/models"
"recipe-manager/services/logger"
"recipe-manager/services/sheet" "recipe-manager/services/sheet"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"go.uber.org/zap"
) )
type RecipeRouter struct { type RecipeRouter struct {
@ -19,6 +23,10 @@ type RecipeRouter struct {
sheetService sheet.SheetService sheetService sheet.SheetService
} }
var (
Log = logger.GetInstance()
)
func NewRecipeRouter(data *data.Data, sheetService sheet.SheetService) *RecipeRouter { func NewRecipeRouter(data *data.Data, sheetService sheet.SheetService) *RecipeRouter {
return &RecipeRouter{ return &RecipeRouter{
data: data, data: data,
@ -170,5 +178,53 @@ func (rr *RecipeRouter) Route(r chi.Router) {
} }
json.NewEncoder(w).Encode(mapResult) json.NewEncoder(w).Encode(mapResult)
}) })
r.Post("/edit/{version}", func(w http.ResponseWriter, r *http.Request) {
Log.Debug("Edit: ", zap.String("path", r.RequestURI))
version := chi.URLParam(r, "version")
target_recipe := rr.data.GetRecipe(version)
Log.Debug("Target => ", zap.Any("target", target_recipe.MachineSetting.ConfigNumber))
// Body
var changes models.Recipe01
err := json.NewDecoder(r.Body).Decode(&changes)
if err != nil {
Log.Error("Decode in request failed: ", zap.Error(err))
}
Log.Debug("Changes: ", zap.Any("changes", changes))
// TODO: find the matched pd
target_menu := rr.data.GetRecipe01ByProductCode(changes.ProductCode)
menu_map := target_menu.ToMap()
change_map := changes.ToMap()
// Find changes
for key, val := range menu_map {
if val != change_map[key] {
menu_map[key] = change_map[key]
}
}
// Apply changes
tempRecipe := models.Recipe01{}
tempRecipe = tempRecipe.FromMap(menu_map)
rr.data.SetValuesToRecipe(tempRecipe)
finalData := rr.data.ExportToJSON()
temp_finalData, err := json.MarshalIndent(finalData, "", " ")
if err != nil {
Log.Error("Error when indent marshal recipe", zap.Error(err))
return
}
os.WriteFile("./cofffeemachineConfig/coffeethai02_"+version+".json", temp_finalData, fs.FileMode(0666))
w.Header().Add("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{
"status": "OK",
})
})
}) })
} }

View file

@ -324,67 +324,8 @@ func (s *Server) createHandler() {
}) })
// r.Get("/mergelogList", func(w http.ResponseWriter, r *http.Request) {
// ch_dir, err := os.ReadDir("cofffeemachineConfig/changelog")
// if err != nil {
// Log.Error("Error while trying to read dir: ", zap.String("dir", "cofffeemachineConfig/changelog"), zap.Error(err))
// http.Error(w, err.Error(), http.StatusInternalServerError)
// }
// displayable := make([]string, 0)
// for _, file := range ch_dir {
// if strings.Contains(file.Name(), ".html") {
// displayable = append(displayable, file.Name()[:len(file.Name())-len(filepath.Ext(file.Name()))])
// }
// }
// w.Header().Set("Content-Type", "application/json")
// w.WriteHeader(http.StatusOK)
// json.NewEncoder(w).Encode(map[string][]string{"dirs": displayable})
// })
r.Get("/listFileInDir/*", func(w http.ResponseWriter, r *http.Request) { r.Get("/listFileInDir/*", func(w http.ResponseWriter, r *http.Request) {
// // socket
// socket, err := upgrader.Upgrade(w, r, nil)
// if err != nil {
// Log.Error("Error while trying to upgrade socket: ", zap.Error(err))
// return
// }
// // TODO: Change to websocket for better performance
// for {
// msg_type, msg, err := socket.ReadMessage()
// if err != nil {
// Log.Warn("Idle ", zap.Any("status", err))
// return
// }
// var msgPayload map[string]interface{}
// err = json.Unmarshal(msg, &msgPayload)
// if err != nil {
// Log.Error("Error while trying to unmarshal message: ", zap.Error(err))
// return
// }
// // topic := msgPayload["topic"].(string)
// Log.Info("Receive message: ", zap.Any("msg_type", msg_type), zap.Any("msg", msgPayload))
// response, err := json.Marshal(map[string]interface{}{
// "message": "ok",
// })
// if err != nil {
// Log.Error("Error at marshalling response: ", zap.Error(err))
// return
// }
// // respons to client
// socket.WriteMessage(msg_type, response)
// }
//spl //spl
spl_path := strings.Split(r.RequestURI, "/") spl_path := strings.Split(r.RequestURI, "/")
if len(spl_path) > 3 { if len(spl_path) > 3 {
@ -437,6 +378,51 @@ func (s *Server) createHandler() {
Log.Debug("Scan dir completed < ", zap.String("path", r.RequestURI)) Log.Debug("Scan dir completed < ", zap.String("path", r.RequestURI))
}) })
r.Get("/get_log_relation", func(w http.ResponseWriter, r *http.Request) {
// Python looker
py_exec, err := exec.LookPath("python")
if err != nil {
Log.Error("Error while trying to find python: ", zap.Error(err))
http.Error(w, err.Error(), http.StatusInternalServerError)
}
merge_timeline, api_err := os.Open("./python_api/merge_timeline.py")
if api_err != nil {
Log.Error("Error while trying to open merge_timeline.json: ", zap.Error(api_err))
http.Error(w, api_err.Error(), http.StatusInternalServerError)
}
defer merge_timeline.Close()
cmd := exec.Command(py_exec, merge_timeline.Name(), "get_relate")
Log.Debug("Command: ", zap.String("command", cmd.String()))
out, err := cmd.CombinedOutput()
Log.Debug("Output: ", zap.String("output", string(out)))
if err != nil {
Log.Error("Error while trying to run python: ", zap.Error(err))
http.Error(w, err.Error(), http.StatusInternalServerError)
}
// clean up output
clean1 := strings.ReplaceAll(string(out), "\n", "")
clean2 := strings.ReplaceAll(clean1, "\r", "")
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]interface{}{
"message": clean2,
})
})
r.Post("/diffpy/*", func(w http.ResponseWriter, r *http.Request) {
Log.Debug("Diffpy: ", zap.String("path", r.RequestURI))
// TODO: add command exec `python_Exec` `merge_recipe.py` `diff` `master_version` `version-version-version` `debug?` `flatten={true|false}` `out={true|false}`
})
sheetService, err := sheet.NewSheetService(context.Background(), s.cfg) sheetService, err := sheet.NewSheetService(context.Background(), s.cfg)
if err != nil { if err != nil {