diff --git a/client/src/app/app-routing.module.ts b/client/src/app/app-routing.module.ts index 4c9622a..ec7e81e 100644 --- a/client/src/app/app-routing.module.ts +++ b/client/src/app/app-routing.module.ts @@ -67,6 +67,10 @@ const routes: Routes = [ }, ], }, + { + path: 'log', + loadComponent: () => import('./features/changelog/changelog.component').then((m) => m.ChangelogComponent), + }, { path: '**', pathMatch: 'full', diff --git a/client/src/app/features/merge/merge-service.service.ts b/client/src/app/features/merge/merge-service.service.ts index 3314fac..f25f8f7 100644 --- a/client/src/app/features/merge/merge-service.service.ts +++ b/client/src/app/features/merge/merge-service.service.ts @@ -7,6 +7,8 @@ export class MergeServiceService { master_version: number = 0; dev_version: number = 0; + output_path:string = ""; + changelog_path:string = ""; constructor() { } } diff --git a/client/src/app/features/merge/merge.component.html b/client/src/app/features/merge/merge.component.html index 6221ca4..2aece9f 100644 --- a/client/src/app/features/merge/merge.component.html +++ b/client/src/app/features/merge/merge.component.html @@ -26,12 +26,24 @@
- +
- + +
+ + +
+ + +
+ + +
+ +
diff --git a/client/src/app/features/merge/merge.component.ts b/client/src/app/features/merge/merge.component.ts index aa8b6fe..c954692 100644 --- a/client/src/app/features/merge/merge.component.ts +++ b/client/src/app/features/merge/merge.component.ts @@ -3,6 +3,7 @@ import { CommonModule } from '@angular/common'; import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; import { MergeServiceService } from './merge-service.service'; +import { HttpClient } from '@angular/common/http'; @Component({ selector: 'app-merge', @@ -17,13 +18,16 @@ export class MergeComponent { mergeForm = this.formBuilder.group({ master_version: 0, - dev_version: 0 + dev_version: 0, + output_path: "", + changelog_path: "" }); constructor( private targets: MergeServiceService, private formBuilder: FormBuilder, + private httpClient: HttpClient, ){} private isException(value: any){ @@ -36,14 +40,47 @@ export class MergeComponent { } this.targets.master_version = this.mergeForm.value.master_version!; this.targets.dev_version = this.mergeForm.value.dev_version!; + this.targets.output_path = this.mergeForm.value.output_path!; + this.targets.changelog_path = this.mergeForm.value.changelog_path!; // TODO: Fetch merge. Modify this to websocket - return fetch("http://localhost:3000/merge", { - method: "POST", - body: JSON.stringify({ - master: this.targets.master_version, - dev: this.targets.dev_version - }) + + this.httpClient.post("http://localhost:8080/merge", { + master: this.targets.master_version, + dev: this.targets.dev_version, + output: this.targets.output_path, + changelog: this.targets.changelog_path + }, { + withCredentials: true + }).subscribe({ + next(value) { + console.log(value) + }, }) } + + downloadLogs(query: string){ + let additionalParams:string = "?query="; + if(query != ""){ + additionalParams += query + } else { + additionalParams = "" + } + this.httpClient.get("http://localhost:8080/dllog"+additionalParams, { responseType: 'blob' }).subscribe( + (data) => { + const blob = new Blob([data], { type: 'application/octet-stream' }); + + const url = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = 'logfile.log'; + document.body.appendChild(a); + a.click(); + window.URL.revokeObjectURL(url); + }, + (error) => { + console.error('Error downloading log file: ',error); + } + ) + } } diff --git a/client/src/assets/google-color.svg:Zone.Identifier b/client/src/assets/google-color.svg:Zone.Identifier deleted file mode 100644 index e69de29..0000000 diff --git a/server/python_api/merge_recipe.py b/server/python_api/merge_recipe.py index dfd5b4e..8fbd643 100644 --- a/server/python_api/merge_recipe.py +++ b/server/python_api/merge_recipe.py @@ -1,78 +1,22 @@ -#!/usr/local/bin/python3 -#!/usr/bin/python3 - -from __future__ import print_function -from functools import reduce -from operator import getitem -import os -import threading -import time -import sys -import subprocess -from io import StringIO import datetime +from functools import reduce import json -import string -from datetime import datetime - - - +from operator import getitem import sys -import os.path +import os +import itertools -from google.auth.transport.requests import Request -from google.oauth2.credentials import Credentials -from google_auth_oauthlib.flow import InstalledAppFlow -from googleapiclient.discovery import build -from googleapiclient.errors import HttpError +# /home/pakin/Codes/coffCfg/cofffeemachineConfig/coffeethai02_1550.json -import google.auth -import UnifyGen - - -if sys.version_info[0] >= 3: - unicode = str - -HomeDirectory = "/Users/wanloprungsiyangkul/" -CoffeeRecipeDirectory = "/Users/wanloprungsiyangkul/cofffeemachineConfig" -valid_len_product_code = len("12-01-02-0001") - -def xl2int(s): - s = s.strip().upper() - return sum((ord(c)-ord('A')+1)*26**i - for i,c in enumerate(reversed(s))) def GetDateTimeString(): - now = datetime.now() # current date and time + now = datetime.datetime.now() # current date and time date_time = now.strftime("%d-%b-%Y, %H:%M:%S") return date_time - -def ImportFileAndUpdateMenuName( recipe_version, menu_name_tsv, output_file): - file_name_recipe = CoffeeRecipeDirectory + "/coffeethai02_" + str(recipe_version) + ".json" - print("file_name_recipe = " + file_name_recipe) - file_tsv = open( menu_name_tsv) #open requested file - - with open( file_name_recipe, 'r') as JSON: - jdatclass = json.load(JSON) - - print("configNumber =" + str( jdatclass["MachineSetting"]["configNumber"])) - jdatclass["Timestamp"] = GetDateTimeString() - - Lines = file_tsv.readlines() - for line in Lines: - count += 1 - sp = line.split('\t') - - #for rp in jdatclass["Recipe01"]: - # if rp["productCode"] == - - - with open( output_file, "w") as outfile_js: - json.dump( jdatclass, outfile_js,indent=4,ensure_ascii=False) - - - +HomeDirectory = "/home/pakin" +CoffeeRecipeDirectory = "/home/pakin/Codes/coffCfg/cofffeemachineConfig" +valid_len_product_code = len("12-01-02-0001") # events - save any action done by merge events = [] @@ -92,8 +36,6 @@ holdonPD = "" def set_value_in_nested_map(key_list: list, value): reduce(getitem, key_list[:-1], master_json)[key_list[-1]] = value -# Get value of nested map by using keys from `key_list` with additional param `isMaster` for extra padding -# in case of different size of length between master and dev. def get_value_in_nested_map(target_json: dict, key_list: list, isMaster=False): if "Recipe01" in key_list: if isMaster: @@ -117,28 +59,24 @@ def decode_path(str_with_dot: str) -> list: keylist.append(keyi) return keylist -# Fetch the product code by giving the path and source def fetch_pd(str_path: str, target_dict: dict) -> str: keyList = decode_path(str_with_dot=str_path) keyList.append("productCode") # print("decode and append get : ",keyList) return get_value_in_nested_map(target_json=target_dict, key_list=keyList) -# Fetch the material id by giving the path and source def fetch_matId(str_path: str, target_dict:dict) -> str: keyList = decode_path(str_with_dot=str_path) keyList.append("materialPathId") # print(keyList) return get_value_in_nested_map(target_json=target_dict, key_list=keyList) -# Fetch the default id by giving the path and source def fetch_defaultId(str_path: str, target_dict:dict) -> str: keyList = decode_path(str_with_dot=str_path) keyList.append("defaultIDSelect") # print(keyList) return get_value_in_nested_map(target_json=target_dict, key_list=keyList) -# Fetch some parts of the path; Ex. Recipe01.8.recipes.0 --> Recipe01.8 def fetch_onlyMainMenuPath(str_with_dot: str): mainpath = decode_path(str_with_dot)[:2] # '.'.join(mainpath) @@ -219,9 +157,42 @@ def merge(args): events.append(GetDateTimeString()+"\t[OUTPUT]\t\tFinished! write output to "+outfile_path+"\n") events.append(GetDateTimeString()+"\t[LOG]\t\tLog is saved to "+changefile_path+"\n") - with open(changefile_path, "a+") as outlogfile: + with open(changefile_path, "a+", encoding="utf-8") as outlogfile: for event in events: outlogfile.write(event) + + # Create html version + with open(changefile_path[:-3]+"html", "a+", encoding="utf-8") as outlogHtml: + for event in events: + # Create div + # print("Log as list: ",str(event).split("\t")) + html_string = "\t
\n" + event_fraction = str(event).split("\t") + for i in event_fraction: + if i != "" and i != "\n" and i != "---": + if "|" in i and not i.endswith("|"): + # CHANGE + spl_text = i.split("|") + html_string += "\t\t

"+spl_text[0]+"

\n" + html_string += "\t\t

"+spl_text[1].replace("\n","")+"

\n" + elif ">>>" in i: + # INSERT + spl_text = i.split(">>>") + html_string += "\t\t

"+spl_text[0]+"

\n" + html_string += "\t\t

"+spl_text[1].replace("\n","")+"

\n" + elif i.endswith("|"): + html_string += "\t\t

"+i[:-1]+"

\n" + else: + # print("Default = ", i) + # Either version, status or others + + html_string += "\t\t

"+i.replace("\n","")+"

\n" + html_string += "\t
\n" + + outlogHtml.write(html_string) + + + # Merge dictionary - called by `merge`, using when the value is `dict` type # original - main file to merge/append value into @@ -271,7 +242,7 @@ def merge_dicts(original:dict, updated:dict, path=""): lc = last_change if last_change != "" else pre_timestamp try: if debug == "debug": - print("Encounter path --> "+path, " | master: ",original[key]," dev: ", value) + print("Encounter path --> "+current_path, " | master: ",original[key]," dev: ", value) except: pass if "Recipe01" in current_path and not "recipes" in current_path: @@ -348,23 +319,13 @@ def merge_lists(original, updated, path=""): pdadd += 1 original.append(item) - - def main(): command_line = sys.argv[1] - if command_line == "name": - UnifyGen.GetFileSheet("menu-name", ".menu-name.tsv") - ImportFileAndUpdateMenuName( 529, ".menu-name.tsv", CoffeeRecipeDirectory + "/coffeethai02_530.json") + print(sys.argv) if command_line == "merge": - dev_version = sys.argv[2] merge(sys.argv[2:]) + if __name__ == "__main__": - main() - - - - -#./import_price.py ~/cofffeemachineConfig/profile_MYR/profile_MYR_1.json ~/Downloads/MYS\ Taobin\ Menu\ with\ price.xlsx\ -\ 28_Mar_2023.tsv F ~/cofffeemachineConfig/profile_MYR/profile_MYR_1_3.json - + main() \ No newline at end of file diff --git a/server/server.go b/server/server.go index d0232f6..3ddf6a5 100644 --- a/server/server.go +++ b/server/server.go @@ -9,6 +9,7 @@ import ( "net/url" "os" "os/exec" + "path/filepath" "recipe-manager/config" "recipe-manager/data" "recipe-manager/routers" @@ -77,9 +78,8 @@ func (s *Server) createHandler() { AllowedOrigins: strings.Split(s.cfg.AllowedOrigins, ","), AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"}, AllowCredentials: true, - AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, - MaxAge: 300, // Maximum value not ignored by any of major browsers - // Debug: true, + ExposedHeaders: []string{"Content-Type"}, + AllowedMethods: []string{"GET", "POST", "PUT", "DELETE"}, })) database := data.NewData() @@ -92,6 +92,7 @@ func (s *Server) createHandler() { // Protect Group r.Group(func(r chi.Router) { + r.Use(func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -121,11 +122,8 @@ func (s *Server) createHandler() { }) }) - // Recipe Router - rr := routers.NewRecipeRouter(database) - rr.Route(r) - r.Post("/merge", func(w http.ResponseWriter, r *http.Request) { + var targetMap map[string]interface{} err := json.NewDecoder(r.Body).Decode(&targetMap) if err != nil { @@ -133,38 +131,72 @@ func (s *Server) createHandler() { log.Fatalln("Merge request failed: ", err) return } - log.Println(targetMap) + repo_path := "cofffeemachineConfig/coffeethai02_" + master_version := targetMap["master"].(string) dev_version := targetMap["dev"].(string) + output_path := targetMap["output"].(string) + changelog_path := targetMap["changelog"].(string) // find target file in the cofffeemachineConfig - if _, err := os.Stat("coffeemachineConfig/coffeethai02_" + master_version + ".json"); err != nil { + if _, err := os.Stat(repo_path + master_version + ".json"); err != nil { w.WriteHeader(http.StatusBadRequest) log.Fatalln("Merge request failed. Master file not found: ", err) return } - if _, err := os.Stat("coffeemachineConfig/coffeethai02_" + dev_version + ".json"); err != nil { + if _, err := os.Stat(repo_path + dev_version + ".json"); err != nil { w.WriteHeader(http.StatusBadRequest) log.Fatalln("Merge request failed. Dev file not found: ", err) return } - repo_path := "coffeemachineConfig/coffeethai02_" master_path := repo_path + master_version + ".json" dev_path := repo_path + dev_version + ".json" - // output path - output_path := "" + // // output path + // output_path := "" - // changelog path - changelog_path := "" + // // changelog path + // changelog_path := "" // TODO: Call merge api if found - err = exec.Command("python", "merge", master_path, dev_path, output_path, changelog_path).Run() + + // lookup for python exec + py_exec, err := exec.LookPath("python") + if err != nil { + log.Fatalln("Python error: ", err) + } else { + py_exec, err = filepath.Abs(py_exec) + } + + log.Println("Found python exec: ", py_exec) + // target api file + merge_api, api_err := os.Open("./python_api/merge_recipe.py") + if api_err != nil { + log.Fatalln("Merge request failed. Python api error: ", api_err) + } + log.Println("Locate python api", merge_api.Name()) + cmd := exec.Command(py_exec, merge_api.Name(), "merge", master_path, dev_path, output_path, changelog_path, "debug") + + log.Println("Run merge command", cmd) + err = cmd.Run() if err != nil { log.Fatalln("Merge request failed. Python merge failed: ", err) } + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(map[string]string{"message": "Merge success"}) + }) + + r.Get("/dllog", func(w http.ResponseWriter, r *http.Request) { + + }) + // Recipe Router + rr := routers.NewRecipeRouter(database) + rr.Route(r) + }) r.NotFound(func(w http.ResponseWriter, r *http.Request) {