sample-ui-component-library 0.0.49-dev → 0.0.51-dev

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sample-ui-component-library",
3
- "version": "0.0.49-dev",
3
+ "version": "0.0.51-dev",
4
4
  "description": "A library which contains sample UI elements that can be used for populating layouts.",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -0,0 +1,7 @@
1
+ let EDITOR_MODES = {
2
+ DESIGN: 1,
3
+ MAPPING: 2
4
+ };
5
+ EDITOR_MODES = Object.freeze(EDITOR_MODES);
6
+
7
+ export default EDITOR_MODES;
@@ -17,12 +17,17 @@ import { EditorContext } from "./EditorContext";
17
17
 
18
18
  import { editorReducer, initialState } from "./EditorReducer";
19
19
 
20
+ const MODES = {
21
+ MAPPING: 1,
22
+ IMPLEMENTATION: 2
23
+ }
24
+
20
25
  /**
21
26
  * Renders the editor component with support for tabs.
22
27
  *
23
28
  * @return {JSX}
24
29
  */
25
- export const Editor = forwardRef(({ }, ref) => {
30
+ export const Editor = forwardRef(({ onSelectAbstraction }, ref) => {
26
31
  const [state, dispatch] = useReducer(editorReducer, initialState);
27
32
 
28
33
  const selectTab = useCallback((id) => {
@@ -45,6 +50,18 @@ export const Editor = forwardRef(({ }, ref) => {
45
50
  dispatch({ type: "RESET_STATE"});
46
51
  dispatch({ type: "SET_PARENT_TAB_GROUP_ID", payload: id });
47
52
  }, []);
53
+
54
+ const setMapping = useCallback((fileName, mapping) => {
55
+ dispatch({ type: "SET_MAPPING", payload: { fileName, mapping } });
56
+ }, []);
57
+
58
+ const setMode = useCallback((mode) => {
59
+ dispatch({ type: "SET_MODE", payload: mode });
60
+ }, []);
61
+
62
+ const setMappedIds = useCallback((ids) => {
63
+ dispatch({ type: "SET_MAPPED_IDS", payload: ids });
64
+ }, []);
48
65
 
49
66
  const api = useMemo(() => {
50
67
  return {
@@ -53,9 +70,12 @@ export const Editor = forwardRef(({ }, ref) => {
53
70
  setTabGroupId,
54
71
  selectTab,
55
72
  closeTab,
56
- moveTab
73
+ moveTab,
74
+ setMapping,
75
+ setMode,
76
+ setMappedIds
57
77
  };
58
- }, [state, addTab, selectTab, closeTab, moveTab, setTabGroupId]);
78
+ }, [state, addTab, selectTab, closeTab, moveTab, setTabGroupId, setMapping, setMode, setMappedIds]);
59
79
 
60
80
  useImperativeHandle(ref, () => api, [api]);
61
81
 
@@ -66,7 +86,7 @@ export const Editor = forwardRef(({ }, ref) => {
66
86
  <Tabs />
67
87
  </div>
68
88
  <div className="monacoContainer">
69
- <MonacoInstance />
89
+ <MonacoInstance onSelectAbstraction={onSelectAbstraction}/>
70
90
  </div>
71
91
  </div>
72
92
  </EditorContext.Provider>
@@ -1,8 +1,13 @@
1
+ import EDITOR_MODES from "./EDITOR_MODES";
2
+
1
3
  export const initialState = {
2
4
  uid: crypto.randomUUID(),
3
5
  tabs: [],
4
6
  activeTab: null,
5
- parentTabGroupId: null
7
+ mode: EDITOR_MODES.DESIGN,
8
+ mapping: new Map(),
9
+ parentTabGroupId: null,
10
+ mappedIds: []
6
11
  };
7
12
 
8
13
  export const editorReducer = (state, action) => {
@@ -99,7 +104,31 @@ export const editorReducer = (state, action) => {
99
104
  tabs: prevTabs
100
105
  };
101
106
  }
102
-
107
+
108
+ case "SET_MAPPING": {
109
+ const { mapping, fileName } = action.payload;
110
+ const newMapping = new Map(state.mapping);
111
+ newMapping.set(fileName, mapping);
112
+ return {
113
+ ...state,
114
+ mapping: newMapping
115
+ };
116
+ }
117
+
118
+ case "SET_MODE": {
119
+ return {
120
+ ...state,
121
+ mode: action.payload
122
+ };
123
+ }
124
+
125
+ case "SET_MAPPED_IDS": {
126
+ return {
127
+ ...state,
128
+ mappedIds: action.payload
129
+ };
130
+ }
131
+
103
132
  case "RESET_STATE": {
104
133
  return initialState;
105
134
  }
@@ -1,13 +1,15 @@
1
- import React, { useEffect, useRef, useState } from 'react';
1
+ import React, { useCallback, useLayoutEffect, useEffect, useRef, useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
 
4
+ import EDITOR_MODES from '../EDITOR_MODES';
5
+
4
6
  import Editor from '@monaco-editor/react';
5
7
 
6
8
  import "./MonacoInstance.scss"
7
9
 
8
10
  import { useEditor } from "../Editor";
9
11
 
10
- export const MonacoInstance = ({ }) => {
12
+ export const MonacoInstance = ({onSelectAbstraction}) => {
11
13
  const { state } = useEditor();
12
14
  const [editorContent, setEditorContent] = useState("Loading content...");
13
15
  const [showEditor, setShowEditor] = useState(false);
@@ -17,14 +19,17 @@ export const MonacoInstance = ({ }) => {
17
19
  const containerRef = useRef(null);
18
20
  const frameRef = useRef(0);
19
21
 
20
- useEffect(() => {
22
+ const [overlayDivs, setOverlayDivs] = useState();
23
+
24
+ useLayoutEffect(() => {
21
25
  if (state.activeTab) {
22
26
  setEditorContent(state.activeTab.content);
23
27
  setShowEditor(true);
28
+ addOverlays();
24
29
  } else {
25
30
  setShowEditor(false);
26
31
  }
27
- }, [state.activeTab]);
32
+ }, [state, editorRef]);
28
33
 
29
34
  useEffect(() => {
30
35
  content.current = editorContent;
@@ -38,13 +43,62 @@ export const MonacoInstance = ({ }) => {
38
43
  * @param {Object} editor
39
44
  * @param {Object} monaco
40
45
  */
41
- const handleEditorDidMount = (editor, monaco) => {
46
+ const handleEditorDidMount = useCallback((editor, monaco) => {
42
47
  editorRef.current = editor;
43
48
  if (content?.current) {
44
49
  editorRef.current.setValue(content.current);
45
50
  }
46
51
  editorRef.current.layout();
47
- }
52
+ addOverlays();
53
+ }, [state.activeTab]);
54
+
55
+ // Add overlays to editor for the given line ranges.
56
+ const addOverlays = useCallback(() => {
57
+ if (!editorRef.current) return;
58
+
59
+ if (state.mode !== EDITOR_MODES.MAPPING || !state.mapping.get(state.activeTab?.name)) {
60
+ setOverlayDivs([]);
61
+ return;
62
+ }
63
+
64
+ const ranges = state.mapping.get(state.activeTab?.name);
65
+ const lineCount = editorRef.current.getModel().getLineCount();
66
+ const lineHeight = editorRef.current.getOption(monaco.editor.EditorOption.lineHeight);
67
+ const divs = [];
68
+
69
+ ranges.forEach((entry) => {
70
+ const top = editorRef.current.getTopForLineNumber(entry.start_line) - editorRef.current.getScrollTop();
71
+ let bottom = editorRef.current.getTopForLineNumber(entry.end_line + 1) - editorRef.current.getScrollTop();
72
+ if (entry.end_line >= lineCount) {
73
+ bottom = bottom + lineHeight;
74
+ }
75
+
76
+ const style= {
77
+ top: top + "px",
78
+ height: (bottom - top) + "px"
79
+ }
80
+ if (state.mappedIds.includes(entry.uid)) {
81
+ style["backgroundColor"] = "rgba(255, 0, 0, 0.3)";
82
+ }
83
+ const overlayDiv = <div
84
+ className="line-block-overlay"
85
+ onClick={(e) => onSelectAbstraction(entry)}
86
+ style={style}>
87
+ </div>;
88
+ divs.push(overlayDiv);
89
+
90
+ });
91
+ setOverlayDivs(divs);
92
+ }, [editorRef?.current, state, onSelectAbstraction]);
93
+
94
+ // Scroll the editor and update overlays on wheel event.
95
+ const handleWheel = useCallback((e) => {
96
+ if (!editorRef.current) return;
97
+ const deltaY = e.deltaY;
98
+ const currentScrollTop = editorRef.current.getScrollTop();
99
+ editorRef.current.setScrollTop(currentScrollTop + deltaY);
100
+ addOverlays();
101
+ }, [state.activeTab, addOverlays]);
48
102
 
49
103
  // Editor options for Monaco Editor.
50
104
  const editorOptions = {
@@ -70,6 +124,7 @@ export const MonacoInstance = ({ }) => {
70
124
  cancelAnimationFrame(frameRef.current);
71
125
  frameRef.current = requestAnimationFrame(() => {
72
126
  editorRef.current?.layout();
127
+ addOverlays();
73
128
  });
74
129
  });
75
130
 
@@ -79,7 +134,7 @@ export const MonacoInstance = ({ }) => {
79
134
  cancelAnimationFrame(frameRef.current);
80
135
  ro.disconnect();
81
136
  };
82
- }, []);
137
+ }, [state.activeTab, addOverlays]);
83
138
 
84
139
  /**
85
140
  * Render the editor if there is an active tab, otherwise render a placeholder message.
@@ -104,6 +159,12 @@ export const MonacoInstance = ({ }) => {
104
159
  return (
105
160
  <div className="editor-container" ref={containerRef}>
106
161
  {renderEditor()}
162
+ {
163
+ state.mode === EDITOR_MODES.MAPPING &&
164
+ <div className="overlay-layer" onWheel={handleWheel}>
165
+ {overlayDivs}
166
+ </div>
167
+ }
107
168
  </div>
108
169
  )
109
170
  }
@@ -11,4 +11,24 @@
11
11
  position:relative;
12
12
  width:100%;
13
13
  height: 100%;
14
+ }
15
+
16
+ .overlay-layer {
17
+ position: absolute;
18
+ left: 0;
19
+ right: 0;
20
+ inset: 0;
21
+ overflow:hidden;
22
+ }
23
+
24
+ .line-block-overlay {
25
+ z-index: 999;
26
+ position: absolute;
27
+ left: 0;
28
+ right: 0;
29
+ }
30
+
31
+ .line-block-overlay:hover {
32
+ background-color: rgba(255, 166, 0, 0.3);
33
+ cursor: pointer;
14
34
  }
@@ -1 +1,2 @@
1
- export * from "./Editor.jsx"
1
+ export * from "./Editor.jsx"
2
+ export * from "./EDITOR_MODES.js"
@@ -1,6 +1,7 @@
1
- import { useEffect, useState, useRef, useLayoutEffect } from "react";
1
+ import { useEffect, useState, useRef, useLayoutEffect, useCallback } from "react";
2
2
  import { Editor } from "../components/Editor";
3
3
  import { useArgs } from "@storybook/preview-api";
4
+ import EDITOR_MODES from "../components/Editor/EDITOR_MODES";
4
5
  import { action } from "@storybook/addon-actions";
5
6
  import {
6
7
  DndContext,
@@ -11,6 +12,9 @@ import {
11
12
  } from "@dnd-kit/core";
12
13
 
13
14
  import WorkspaceSampleTree from "./data/FileBrowser/workspace_sample.json"
15
+ import transactiondb_mapping from "./data/Mapping/TransactionDB_mapping.json"
16
+ import { ToolBarEditor } from "./components/ToolBarEditor/ToolBarEditor";
17
+ import translator_mapping from "./data/Mapping/FrenchTranslator_mapping.json"
14
18
 
15
19
  import "./EditorStories.scss"
16
20
 
@@ -41,6 +45,11 @@ const Template = (args) => {
41
45
  const [, updateArgs] = useArgs();
42
46
 
43
47
  const [dragPreviewLabel, setDragPreviewLabel] = useState(<></>);
48
+ const [selectTool, setSelectTool] = useState("select");
49
+ const [mappedIds, setMappedIds] = useState([
50
+ "c4f43010-71ef-46e6-bf38-0548d3a34012",
51
+ "8bf2605a-1940-49de-9b2f-0efa22bf658c"
52
+ ]);
44
53
 
45
54
  const editorRef = useRef();
46
55
 
@@ -52,18 +61,16 @@ const Template = (args) => {
52
61
  }
53
62
  });
54
63
 
55
-
56
- const node = {
57
- "name": "SAMPLE",
58
- "type": "file",
59
- "uid": "dissr-f6459410-1634-4dbc-8d76-35896822158d",
60
- "content": "1234"
61
- }
62
-
63
- editorRef.current.addTab(node,2);
64
+ const result = flattenTree(WorkspaceSampleTree.tree).find(
65
+ (obj) => obj.name === "TransactionDB.py"
66
+ );
67
+ editorRef.current.addTab(result);translator_mapping
68
+ editorRef.current.setMapping("TransactionDB.py", transactiondb_mapping);
69
+ editorRef.current.setMapping("FrenchTranslator.py", translator_mapping);
70
+ editorRef.current.setMappedIds(mappedIds);
64
71
  }, []);
65
72
 
66
- const [dragging, setDragging] = useState(false);
73
+ const [dragging, setDragging] = useState(false);
67
74
 
68
75
  /**
69
76
  * Callback for when drag ends.
@@ -105,10 +112,21 @@ const [dragging, setDragging] = useState(false);
105
112
 
106
113
  window.addEventListener("pointermove", handleMove);
107
114
 
115
+ const handleKeyDown = (event) => {
116
+ console.log("KEY DOWN");
117
+ if (event.code === "KeyA") {
118
+ editorRef.current.setMode(EDITOR_MODES.DESIGN);
119
+ } else if (event.code === "KeyB") {
120
+ editorRef.current.setMode(EDITOR_MODES.MAPPING);
121
+ }
122
+ };
123
+ document.addEventListener("keydown", handleKeyDown);
124
+
108
125
  return () => {
126
+ document.removeEventListener("keydown", handleKeyDown);
109
127
  window.removeEventListener("pointermove", handleMove);
110
128
  };
111
- }, [dragging]);
129
+ }, [dragging, editorRef]);
112
130
 
113
131
  const sensors = useSensors(
114
132
  useSensor(PointerSensor, {
@@ -118,23 +136,48 @@ const [dragging, setDragging] = useState(false);
118
136
  })
119
137
  );
120
138
 
139
+ const onSelectTool = useCallback((tool) => {
140
+ if (tool === "mapping-mode") {
141
+ editorRef.current.setMode(EDITOR_MODES.MAPPING);
142
+ } else if (tool === "implementation-mode") {
143
+ editorRef.current.setMode(EDITOR_MODES.DESIGN);
144
+ }
145
+ }, [editorRef]);
146
+
147
+ const onSelectAbstraction = useCallback((entry) => {
148
+ action("Abstraction Selected")(entry);
149
+ const found = mappedIds.find((id) => id === entry.uid);
150
+ const newMap = (found)?
151
+ mappedIds.filter((id) => id !== found):
152
+ [...mappedIds, entry.uid];
153
+ setMappedIds(newMap);
154
+ editorRef.current.setMappedIds(newMap);
155
+ }, [mappedIds, setMappedIds, editorRef]);
156
+
121
157
  return (
122
158
  <DndContext sensors={sensors} onDragStart={onDragStart} onDragEnd={handleDragEnd}>
123
- <div className="editorStoryWrapper">
124
- <Editor ref={editorRef}{...args} />
125
- </div>
126
- {dragging && (
127
- <div
128
- style={{
129
- position: "fixed",
130
- left: pos.x,
131
- top: pos.y,
132
- pointerEvents: "none",
133
- zIndex: 9999,
134
- }}>
135
- {dragPreviewLabel}
159
+
160
+ <div className="editorRootContainer">
161
+ <div className="toolbar">
162
+ <ToolBarEditor onSelectTool={onSelectTool} />
163
+ </div>
164
+ <div className="flow">
165
+ <Editor ref={editorRef} onSelectAbstraction={onSelectAbstraction} {...args} />
166
+ {dragging && (
167
+ <div
168
+ style={{
169
+ position: "fixed",
170
+ left: pos.x,
171
+ top: pos.y,
172
+ pointerEvents: "none",
173
+ zIndex: 9999,
174
+ }}>
175
+ {dragPreviewLabel}
176
+ </div>
177
+ )}
136
178
  </div>
137
- )}
179
+ </div>
180
+
138
181
  </DndContext>
139
182
  )
140
183
  }
@@ -1,7 +1,22 @@
1
- .editorStoryWrapper{
1
+
2
+
3
+ .editorRootContainer {
2
4
  position: absolute;
3
5
  top:0;
4
6
  bottom:0;
5
7
  left:0;
6
8
  right:0;
7
- }
9
+ display:flex;
10
+ flex-direction: row;
11
+ }
12
+
13
+ .toolbar {
14
+ width:40px;
15
+ }
16
+
17
+ .flow {
18
+ display: flex;
19
+ flex-grow: 1;
20
+ flex-direction: column;
21
+ overflow:hidden;
22
+ }
@@ -0,0 +1,38 @@
1
+ import React, { useState } from "react";
2
+
3
+ import { Alt, Crosshair } from "react-bootstrap-icons";
4
+
5
+ import "./ToolBarEditor.scss";
6
+
7
+ ToolBarEditor.propTypes = {};
8
+
9
+ /**
10
+ * ToolBarEditor Component
11
+ * @return {JSX.Element}
12
+ */
13
+ export function ToolBarEditor({ onSelectTool }) {
14
+ const [selectedTool, setSelectedTool] = useState("select");
15
+
16
+ const selectTool = (tool) => {
17
+ setSelectedTool(tool);
18
+ onSelectTool(tool);
19
+ };
20
+
21
+ return (
22
+ <div className="toolbarWrapper">
23
+ <div className="toolbarContainer">
24
+ <Alt
25
+ onClick={(e) => selectTool("implementation-mode")}
26
+ title="Implementation Mode"
27
+ className="icon"
28
+ />
29
+ <Crosshair
30
+ onClick={(e) => selectTool("mapping-mode")}
31
+ title="Mapping Mode"
32
+ className="icon"
33
+ />
34
+ </div>
35
+ <div className="toolbarContainer bottom"></div>
36
+ </div>
37
+ );
38
+ }
@@ -0,0 +1,29 @@
1
+ .toolbarWrapper {
2
+ display:flex;
3
+ flex-direction: column;
4
+ justify-content:space-around;
5
+ height: 100%;
6
+ background-color: #333333;
7
+ }
8
+
9
+ .toolbarContainer {
10
+ display: flex;
11
+ flex-direction: column;
12
+ gap: 15px;
13
+ width:100%;
14
+ align-items: center;
15
+ padding: 10px 0;
16
+ }
17
+
18
+ .toolbarWrapper > .bottom {
19
+ margin-top: auto;
20
+ }
21
+
22
+ .toolbarContainer > .icon {
23
+ color:grey;
24
+ }
25
+
26
+ .toolbarContainer > .icon:hover {
27
+ color:white;
28
+ cursor:pointer;
29
+ }
@@ -1 +1,66 @@
1
- {"tree":[{"name":"library_manager_single_threaded","type":"folder","uid":"dir-ba56de84-31dd-4ccf-ae98-a98b0581f803","children":[{"name":"library_manager.py","type":"file","uid":"dir-3d7aaf8e-6224-4eb7-8301-936447fef430","content":"import sys\n\ndef place_book_on_shelf(book_shelf, name, genre):\n '''\n This function places the given book on the shelf \n in a slot determined by the first letter of its name.\n \n :param book_shelf: Bookshelf to place the book on.\n :param name: Name of the book.\n :param genre: Genre of the book.\n '''\n print(f\"Accepted book: {name} (Genre: {genre})\")\n \n firstLetter = name[0].upper()\n\n if (firstLetter not in book_shelf):\n book_shelf[firstLetter] = []\n else:\n print(\"Slot for book already exists.\")\n\n book_shelf[firstLetter].append(name)\n\n return book_shelf\n\ndef accept_book():\n '''\n Accepts a book and genre from the user and returns it.\n '''\n print(\"\\nEnter book details:\")\n \n name = input(\"Book name: \")\n\n genre = input(\"Genre: \")\n\n return {\"name\":name, \"genre\":genre}\n\n\ndef library_manager():\n '''\n Runs the library manager to accept books until the user decides to stop.\n '''\n book_shelf = {}\n\n while True:\n book_details = accept_book()\n book_shelf = place_book_on_shelf(\n book_shelf, \n book_details[\"name\"], \n book_details[\"genre\"]\n )\n\n more = input(\"Add another book? (y): \").lower()\n \n if more != \"y\":\n break\n\n print(\"Exiting library manager, goodbye.\")\n\nif __name__ == \"__main__\":\n sys.exit(library_manager())\n "},{"name":"readme","type":"file","uid":"dir-5aa507ee-e43d-4d77-b49a-b6e0b1da36f5","content":"This is version 1 of the library manager and it contains a simple implementation that accepts a book name and genre before placing it on the shelf. It places it on the shelf using the first letter of the book name. It continues to accept books until the user decides to stop.\n\nThis version of the library manager will also use instrumentation to identify abstractions by line to overcome the limitations of existing techniques."}]},{"name":"text_translator","type":"folder","uid":"dir-cbb97582-e3da-403e-aa7f-a524a5a7cff7","children":[{"name":"FrenchTranslator.py","type":"file","uid":"dir-516008b2-e80c-4a3a-bb00-d651885ac15d","content":"import threading\nimport queue\nfrom deep_translator import GoogleTranslator # type: ignore\n\nclass FrenchTranslator(threading.Thread):\n def __init__(self, id, queue, packerQueue):\n super().__init__(daemon=True)\n self.queue = queue\n self.id = id\n self.packerQueue = packerQueue\n self.start()\n\n def run(self):\n while True:\n try:\n job = self.queue.get(timeout=10)\n except queue.Empty:\n continue\n\n translatedMsg = GoogleTranslator(source=\"auto\", target=\"french\").translate(job[\"value\"][\"data\"])\n self.packerQueue.put({\n \"uid\": job[\"uid\"],\n \"value\": {\n \"language\": \"french\",\n \"original\": job[\"value\"][\"data\"],\n \"translated\": translatedMsg\n }\n })"},{"name":"Packer.py","type":"file","uid":"dir-e5085241-ff4f-434d-85ab-7f50e673ea4d","content":"import threading\nimport queue\nfrom TransactionDB import TransactionDB\n\nclass PackerThread(threading.Thread):\n def __init__(self, queue):\n super().__init__(daemon=True)\n self.queue = queue\n self.items = {}\n self.start()\n\n def run(self):\n self.db = TransactionDB()\n while True:\n try:\n translatedJobPart = self.queue.get(timeout=10)\n except queue.Empty:\n continue\n\n self.db.addTranslation(translatedJobPart)\n\n if self.db.isDone(translatedJobPart[\"uid\"]):\n self.db.setDone(translatedJobPart[\"uid\"])"},{"name":"SpanishTranslator.py","type":"file","uid":"dir-b2a78dfd-aa94-4068-a5f7-24c396d88825","content":"import threading\nimport queue\nfrom deep_translator import GoogleTranslator # type: ignore\n\nclass SpanishTranslator(threading.Thread):\n def __init__(self, id, queue, packerQueue):\n super().__init__(daemon=True)\n self.queue = queue\n self.id = id\n self.packerQueue = packerQueue\n self.start()\n\n def run(self):\n while True:\n try:\n job = self.queue.get(timeout=10)\n except queue.Empty:\n continue\n\n translatedMsg = GoogleTranslator(source=\"auto\", target=\"spanish\").translate(job[\"value\"][\"data\"])\n self.packerQueue.put({\n \"uid\": job[\"uid\"],\n \"value\": {\n \"language\": \"spanish\",\n \"original\": job[\"value\"][\"data\"],\n \"translated\": translatedMsg\n }\n })"},{"name":"TamilTranslator.py","type":"file","uid":"dir-594ff411-b916-4a88-9077-3d5b8ff7f871","content":"import threading\nimport queue\nfrom deep_translator import GoogleTranslator # type: ignore\n\nclass TamilTranslator(threading.Thread):\n def __init__(self, id, queue, packerQueue):\n super().__init__(daemon=True)\n self.queue = queue\n self.id = id\n self.packerQueue = packerQueue\n self.start()\n\n def run(self):\n while True:\n try:\n job = self.queue.get(timeout=10)\n except queue.Empty:\n continue\n\n translatedMsg = GoogleTranslator(source=\"auto\", target=\"tamil\").translate(job[\"value\"][\"data\"])\n self.packerQueue.put({\n \"uid\": job[\"uid\"],\n \"value\": {\n \"language\": \"tamil\",\n \"original\": job[\"value\"][\"data\"],\n \"translated\": translatedMsg\n }\n })"},{"name":"readme","type":"file","uid":"dir-f6459410-1634-4dbc-8d76-35896822158d","content":"This folder contains a text translator that uses three workers to translate the text and uses a packer to add the results to a cabinet."}]}]}
1
+ {
2
+ "tree": [
3
+ {
4
+ "name": "library_manager_single_threaded",
5
+ "type": "folder",
6
+ "uid": "dir-ba56de84-31dd-4ccf-ae98-a98b0581f803",
7
+ "children": [
8
+ {
9
+ "name": "library_manager.py",
10
+ "type": "file",
11
+ "uid": "dir-3d7aaf8e-6224-4eb7-8301-936447fef430",
12
+ "content": "import sys\n\ndef place_book_on_shelf(book_shelf, name, genre):\n '''\n This function places the given book on the shelf \n in a slot determined by the first letter of its name.\n \n :param book_shelf: Bookshelf to place the book on.\n :param name: Name of the book.\n :param genre: Genre of the book.\n '''\n print(f\"Accepted book: {name} (Genre: {genre})\")\n \n firstLetter = name[0].upper()\n\n if (firstLetter not in book_shelf):\n book_shelf[firstLetter] = []\n else:\n print(\"Slot for book already exists.\")\n\n book_shelf[firstLetter].append(name)\n\n return book_shelf\n\ndef accept_book():\n '''\n Accepts a book and genre from the user and returns it.\n '''\n print(\"\\nEnter book details:\")\n \n name = input(\"Book name: \")\n\n genre = input(\"Genre: \")\n\n return {\"name\":name, \"genre\":genre}\n\n\ndef library_manager():\n '''\n Runs the library manager to accept books until the user decides to stop.\n '''\n book_shelf = {}\n\n while True:\n book_details = accept_book()\n book_shelf = place_book_on_shelf(\n book_shelf, \n book_details[\"name\"], \n book_details[\"genre\"]\n )\n\n more = input(\"Add another book? (y): \").lower()\n \n if more != \"y\":\n break\n\n print(\"Exiting library manager, goodbye.\")\n\nif __name__ == \"__main__\":\n sys.exit(library_manager())\n "
13
+ },
14
+ {
15
+ "name": "readme",
16
+ "type": "file",
17
+ "uid": "dir-5aa507ee-e43d-4d77-b49a-b6e0b1da36f5",
18
+ "content": "This is version 1 of the library manager and it contains a simple implementation that accepts a book name and genre before placing it on the shelf. It places it on the shelf using the first letter of the book name. It continues to accept books until the user decides to stop.\n\nThis version of the library manager will also use instrumentation to identify abstractions by line to overcome the limitations of existing techniques."
19
+ }
20
+ ]
21
+ },
22
+ {
23
+ "name": "text_translator",
24
+ "type": "folder",
25
+ "uid": "dir-cbb97582-e3da-403e-aa7f-a524a5a7cff7",
26
+ "children": [
27
+ {
28
+ "name": "FrenchTranslator.py",
29
+ "type": "file",
30
+ "uid": "dir-516008b2-e80c-4a3a-bb00-d651885ac15d",
31
+ "content": "import threading\nimport queue\nfrom deep_translator import GoogleTranslator # type: ignore\n\nclass FrenchTranslator(threading.Thread):\n def __init__(self, id, queue, packerQueue):\n super().__init__(daemon=True)\n self.queue = queue\n self.id = id\n self.packerQueue = packerQueue\n self.start()\n\n def run(self):\n while True:\n try:\n job = self.queue.get(timeout=10)\n except queue.Empty:\n continue\n\n translatedMsg = GoogleTranslator(source=\"auto\", target=\"french\").translate(job[\"value\"][\"data\"])\n self.packerQueue.put({\n \"uid\": job[\"uid\"],\n \"value\": {\n \"language\": \"french\",\n \"original\": job[\"value\"][\"data\"],\n \"translated\": translatedMsg\n }\n })"
32
+ },
33
+ {
34
+ "name": "Packer.py",
35
+ "type": "file",
36
+ "uid": "dir-e5085241-ff4f-434d-85ab-7f50e673ea4d",
37
+ "content": "import threading\nimport queue\nfrom TransactionDB import TransactionDB\n\nclass PackerThread(threading.Thread):\n def __init__(self, queue):\n super().__init__(daemon=True)\n self.queue = queue\n self.items = {}\n self.start()\n\n def run(self):\n self.db = TransactionDB()\n while True:\n try:\n translatedJobPart = self.queue.get(timeout=10)\n except queue.Empty:\n continue\n\n self.db.addTranslation(translatedJobPart)\n\n if self.db.isDone(translatedJobPart[\"uid\"]):\n self.db.setDone(translatedJobPart[\"uid\"])"
38
+ },
39
+ {
40
+ "name": "SpanishTranslator.py",
41
+ "type": "file",
42
+ "uid": "dir-b2a78dfd-aa94-4068-a5f7-24c396d88825",
43
+ "content": "import threading\nimport queue\nfrom deep_translator import GoogleTranslator # type: ignore\n\nclass SpanishTranslator(threading.Thread):\n def __init__(self, id, queue, packerQueue):\n super().__init__(daemon=True)\n self.queue = queue\n self.id = id\n self.packerQueue = packerQueue\n self.start()\n\n def run(self):\n while True:\n try:\n job = self.queue.get(timeout=10)\n except queue.Empty:\n continue\n\n translatedMsg = GoogleTranslator(source=\"auto\", target=\"spanish\").translate(job[\"value\"][\"data\"])\n self.packerQueue.put({\n \"uid\": job[\"uid\"],\n \"value\": {\n \"language\": \"spanish\",\n \"original\": job[\"value\"][\"data\"],\n \"translated\": translatedMsg\n }\n })"
44
+ },
45
+ {
46
+ "name": "TamilTranslator.py",
47
+ "type": "file",
48
+ "uid": "dir-594ff411-b916-4a88-9077-3d5b8ff7f871",
49
+ "content": "import threading\nimport queue\nfrom deep_translator import GoogleTranslator # type: ignore\n\nclass TamilTranslator(threading.Thread):\n def __init__(self, id, queue, packerQueue):\n super().__init__(daemon=True)\n self.queue = queue\n self.id = id\n self.packerQueue = packerQueue\n self.start()\n\n def run(self):\n while True:\n try:\n job = self.queue.get(timeout=10)\n except queue.Empty:\n continue\n\n translatedMsg = GoogleTranslator(source=\"auto\", target=\"tamil\").translate(job[\"value\"][\"data\"])\n self.packerQueue.put({\n \"uid\": job[\"uid\"],\n \"value\": {\n \"language\": \"tamil\",\n \"original\": job[\"value\"][\"data\"],\n \"translated\": translatedMsg\n }\n })"
50
+ },
51
+ {
52
+ "name": "readme",
53
+ "type": "file",
54
+ "uid": "dir-f6459410-1634-4dbc-8d76-35896822158d",
55
+ "content": "This folder contains a text translator that uses three workers to translate the text and uses a packer to add the results to a cabinet."
56
+ },
57
+ {
58
+ "name": "TransactionDB.py",
59
+ "type": "file",
60
+ "uid": "dir-f6459410-1634-4dbc-8d76-35sfdvsd6822158d",
61
+ "content": "import sqlite3\nimport atexit\nimport uuid\n\nclass TransactionDB():\n\n def __init__(self):\n atexit.register(self.cleanup_function)\n self.initializeDB()\n\n def cleanup_function(self):\n self.conn.close()\n\n def initializeDB(self):\n\n self.conn = sqlite3.connect(\"translations.db\")\n self.cursor = self.conn.cursor()\n\n self.cursor.execute(\"\"\"\n CREATE TABLE IF NOT EXISTS translation_jobs (\n job_id INTEGER PRIMARY KEY AUTOINCREMENT,\n uid TEXT NOT NULL UNIQUE,\n english TEXT NOT NULL,\n french TEXT,\n spanish TEXT,\n tamil TEXT,\n done INTEGER NOT NULL DEFAULT 0\n );\n \"\"\")\n self.conn.commit()\n\n def addJob(self, data):\n uid = str(uuid.uuid4())\n self.conn.execute(\n \"\"\"\n INSERT INTO translation_jobs (uid, english)\n VALUES (?, ?)\n \"\"\",\n (uid, data)\n )\n self.conn.commit()\n\n return {\n \"uid\": uid,\n \"value\": {\"data\": data}\n }\n \n def addTranslation(self, msg):\n self.conn.execute(\n f\"\"\"\n UPDATE translation_jobs\n SET {msg[\"value\"][\"language\"]} = ?\n WHERE uid = ?\n \"\"\",\n (msg[\"value\"][\"translated\"], msg[\"uid\"])\n )\n self.conn.commit()\n\n\n def isDone(self, uid):\n cur = self.conn.execute(\n \"\"\"\n SELECT\n french IS NOT NULL\n AND spanish IS NOT NULL\n AND tamil IS NOT NULL\n FROM translation_jobs\n WHERE uid = ?\n \"\"\",\n (uid,)\n )\n row = cur.fetchone()\n return bool(row[0]) if row else False\n \n def setDone(self, uid):\n self.conn.execute(\n \"\"\"\n UPDATE translation_jobs\n SET done = 1\n WHERE uid = ?\n \"\"\",\n (uid,)\n )\n self.conn.commit()\n\n\n "
62
+ }
63
+ ]
64
+ }
65
+ ]
66
+ }