Skip to content
This repository has been archived by the owner on Dec 9, 2024. It is now read-only.

Commit

Permalink
Merged updates from upstream repo.
Browse files Browse the repository at this point in the history
  • Loading branch information
Rumsie-Grimshaw committed Jun 11, 2024
2 parents bc94387 + 919ce83 commit 4fde641
Show file tree
Hide file tree
Showing 13 changed files with 328 additions and 46 deletions.
3 changes: 3 additions & 0 deletions app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ def update_settings():
def reset_settings():
print("Current working directory:", os.getcwd())
# Delete the existing config.ini file
os.chdir(str(utils.APP_DIR))
if os.path.exists('config.ini'):
os.remove('config.ini')
shutil.copy('config.example.ini', 'config.ini')
Expand Down Expand Up @@ -277,6 +278,8 @@ def update_tesseract_path():
if __name__ == "__main__":
host = "localhost"
port = 5000
if os.name == 'posix':
port = 5001
logging.basicConfig(filename="app.log", filemode="w", level=logging.DEBUG, format="%(levelname)s - %(message)s")
print("[*] Starting OcrRoo Server")
print(f"[*] OcrRoo Server running on http://{host}:{port}/")
Expand Down
116 changes: 77 additions & 39 deletions app/utils.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import hashlib
import json
import os.path
from pathlib import Path
import shutil
import subprocess
import logging
import time
import cv2
from json import JSONDecodeError
from typing import Union, Optional
from typing import Union, Optional, Any, List, Dict
import openai
import pytesseract
from pytube import YouTube
Expand All @@ -29,6 +30,9 @@ def playsound_notification(audio_file):
if audio_file is not None:
playsound(file_path)

FILE_PATH = Path(__file__).resolve() # Absolute Path of utils.py
APP_DIR = FILE_PATH.parent
PROJ_ROOT = APP_DIR.parent

def config(section: str = None, option: str = None) -> Union[ConfigParser, str]:
"""
Expand All @@ -39,11 +43,14 @@ def config(section: str = None, option: str = None) -> Union[ConfigParser, str]:
:param option: [Optional] Key/option of value to retrieve
:return: Return string or ConfigParser object
"""

if (section is None) != (option is None):
raise SyntaxError("section AND option parameters OR no parameters must be passed to function config()")
parser = ConfigParser()
if not os.path.exists("config.ini"):
shutil.copy("config.example.ini", "config.ini")
if not (APP_DIR / 'config.ini').exists():
src_path = str(APP_DIR / 'config.example.ini')
dst_path = str(APP_DIR / 'config.ini')
shutil.copy(src_path, dst_path)
parser.read("config.ini")
if parser.get("AppSettings", "openai_api_key") != "your_openai_api_key_here":
openai.api_key = parser.get("AppSettings", "openai_api_key")
Expand All @@ -64,7 +71,8 @@ def hash_video_file(filename: str) -> str:
:return: Returns hex based md5 hash
"""
hash_md5 = hashlib.md5()
with open(f"{get_vid_save_path()}{filename}", "rb") as f:
video_file_path = Path(get_vid_save_path(), f'{filename}').resolve()
with video_file_path.open('rb') as f:
for chunk in iter(lambda: f.read(4096), b""):
hash_md5.update(chunk)
return hash_md5.hexdigest()
Expand Down Expand Up @@ -92,45 +100,64 @@ def format_timestamp(seconds: int) -> str:
return f'{str(minutes).zfill(2)}:{str(remaining_seconds).zfill(2)}'


def read_user_data() -> json:
def read_user_data() -> Optional[Any]:
"""
Reads the users data from json file
:return: Returns user data as json
"""
if not os.path.exists("data\\userdata.json"):
if not os.path.exists("data\\"):
os.makedirs("data\\")
with open("data\\userdata.json", "w") as user_data:
user_data_path = APP_DIR / 'data' / 'userdata.json'
if not user_data_path.exists():
data_dir = APP_DIR / 'data'
if not data_dir.exists():
data_dir.mkdir(parents=True, exist_ok=True) # data directory
with user_data_path.open('w') as user_data:
user_data.write(json.dumps({"all_videos": []}))
pass
return None
try:
with open("data\\userdata.json", "r") as user_data_json:
with user_data_path.open('r') as user_data_json:
data = json.load(user_data_json)
return data
except JSONDecodeError:
logging.error("Failed to read data from userdata.json, file may be empty.")
return None


def directory_append_slash(directory: str) -> str:
"""
Append a trailing slash to a directory path if it doesn't already have one.
:param directory: The directory path to which a trailing slash will be appended, if missing.
:return: The directory path with a trailing slash appended, if necessary.
"""

# Check if directory already have trailing slash
if directory.endswith(('/', '\\')):
return directory

# Append trailing slash
directory += '\\' if os.name == 'nt' else '/'

return directory


def get_vid_save_path() -> str:
"""
Returns output path from config variables, will set default to root of project\\out\\videos\\
:return: file path as string
"""
vid_download_path = config("UserSettings", "video_save_path")

# Set default output path for video download path
if vid_download_path == "output_path":
default_path = os.path.dirname(os.getcwd()) + "\\out\\videos\\"
if not os.path.exists(default_path):
os.makedirs(default_path)
return default_path
# Check if the path ends with a backslash
if not vid_download_path.endswith("\\"):
# If it doesn't end with a backslash, append one
vid_download_path += "\\"
default_path = PROJ_ROOT / 'out' / 'videos'
if not default_path.exists():
default_path.mkdir(parents=True, exist_ok=True)

default_path = str(default_path)
return directory_append_slash(default_path)

return vid_download_path
vid_download_path = str(Path(vid_download_path))

return directory_append_slash(vid_download_path)


def get_output_path() -> str:
Expand All @@ -140,16 +167,18 @@ def get_output_path() -> str:
"""
output_path = config("UserSettings", "capture_output_path")
# Set default output path for code files

if output_path == "output_path":
default_path = os.path.dirname(os.getcwd()) + "\\out\\"
if not os.path.exists(default_path):
os.makedirs(default_path)
return default_path
# Check if the path ends with a backslash
if not output_path.endswith("\\"):
# If it doesn't end with a backslash, append one
output_path += "\\"
return output_path
default_path = PROJ_ROOT / 'out'
if not default_path.exists():
default_path.mkdir(parents=True, exist_ok=True)

default_path = str(default_path)

return directory_append_slash(default_path)

output_path = str(Path(output_path))
return directory_append_slash(output_path)


def send_code_snippet_to_ide(filename: str, code_snippet: str) -> bool:
Expand Down Expand Up @@ -281,7 +310,8 @@ def update_user_video_data(filename: str, progress: Optional[float] = None, capt
record["progress"] = round(progress)
if capture is not None:
record["captures"].append(capture)
with open("data/userdata.json", "w") as json_data:

with (APP_DIR / 'data' / 'userdata.json').open('w') as json_data:
json.dump(user_data, json_data, indent=4)


Expand All @@ -296,7 +326,9 @@ def add_video_to_user_data(filename: str, video_title: str, video_hash: str, you
user_data = read_user_data()
if user_data is None:
return
video_capture = cv2.VideoCapture(f'{get_vid_save_path()}{filename}')

video_path = str(Path(get_vid_save_path(), f'{filename}').resolve())
video_capture = cv2.VideoCapture(video_path)
if not video_capture.isOpened():
logging.error(f"Failed to open video capture for {filename}")
return
Expand All @@ -309,9 +341,13 @@ def add_video_to_user_data(filename: str, video_title: str, video_hash: str, you
return
thumbnail = str(int(time.time())) + ".png"
# Check if img dir exists if not create
if not os.path.exists("static/img"):
os.makedirs("static/img")
cv2.imwrite(f"static/img/{thumbnail}", frame)

static_dir = APP_DIR / 'static'
img_dir = static_dir / 'img'
if not img_dir.exists():
img_dir.mkdir(parents=True, exist_ok=True)

cv2.imwrite(str(img_dir / f'{thumbnail}'), frame)
new_video = {
"video_hash": video_hash,
"filename": filename,
Expand All @@ -325,7 +361,7 @@ def add_video_to_user_data(filename: str, video_title: str, video_hash: str, you
new_video["youtube_url"] = youtube_url
video_capture.release()
user_data["all_videos"].append(new_video)
with open("data/userdata.json", "w") as json_data:
with (APP_DIR / 'data' / 'userdata.json').open('w') as json_data:
json.dump(user_data, json_data, indent=4)


Expand All @@ -344,7 +380,7 @@ def file_already_exists(video_hash: str) -> bool:
return False


def get_setup_progress() -> [str]:
def get_setup_progress() -> List[str]:
"""
Gets users set up progress from config file
:return: Returns array of string containing strings relating to settings that are already set up
Expand All @@ -362,7 +398,7 @@ def get_setup_progress() -> [str]:
return setup_progress


def parse_video_data() -> []:
def parse_video_data() -> dict:
"""
Gets all video data from userdata storage and parses all data for in progress videos
:return: Array containing two arrays, 1 with all videos 1 with in progress videos
Expand Down Expand Up @@ -459,7 +495,8 @@ def delete_video_from_userdata(filename: str) -> None:
if current_video["filename"] == filename:
all_videos.remove(current_video)
break
with open("data/userdata.json", "w") as json_data:

with (APP_DIR / 'data' / 'userdata.json').open('w') as json_data:
json.dump(user_data, json_data, indent=4)


Expand All @@ -479,8 +516,9 @@ def update_configuration(new_values_dict) -> None:
if isinstance(value, bool) or isinstance(value, int):
value = str(value)
config_file.set(section, key, value)

# save the file
with open('config.ini', 'w') as config_file_save:
with (APP_DIR / 'config.ini').open('w') as config_file_save:
config_file.write(config_file_save)


Expand Down
108 changes: 108 additions & 0 deletions design-evidencing-guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Overview

As part of the project, you and your team are required to evidence that you have **designed** an advanced user interface that is fit for purpose.

The following guide will help you ensure that you have met the evidencing requirements while ensuring that you can work in an agile/iterative way that is appropriate for the project and modern software development practices.

## Design Evidencing

Modern design approaches are lightweight and combine iterations with user feedback. This is in contrast to traditional design approaches that are heavy on documentation and require a lot of up-front work.

However, often we cannot access the user in the frequency that allows rapid iteration. One way to mitigate this is to develop personas and scenarios that represent the user and their goals. This allows you to make design decisions based on the user's needs and goals.

### Minimum Requirements

1. In your project repository, create a folder called `design`.
2. In the `design` folder, create a file called `persona.md` that describes the key persona your team was focused on implementing the design (see below for detail)
3. Create a subfolder with any design artefacts you created: wireframes, sketches, mockups, etc.
4. If you are treating the application itself as a prototype, highlight what changes if any you made to the application by referencing the appropriate issues
5. Create at least three github issues related to the design of the application:
1. Tag each issue with ui-design
2. Assign each issue to a team member
3. Include a user story in the format "As a [persona], I want to [goal], so that [reason]"
4. Add any non-functional requirements as notes in the issue
6. Copy this document into your project and answer any relevant questions

## Personas

A persona is a fictional character that represents a user. It is a way to describe the user's goals, needs, and behaviors. They are focused on **empathy** and **understanding** the user, not demographics, and not a collection of features.

> Describe the key persona your team was focused on implementing the design. You can describe the persona in a file called `persona.md` in the `design` folder.
>
Pick the most representative persona from your group. Write a brief (2-3 sentence) justification for why you chose this persona.

"I want to be able to access all the codes at once. I'm busy with school and work, I don't have much time."

### Persona Template

This is an optional template for how to structure your persona:

```markdown
# Persona: [Persona Name]

## Background
Give the person's background - make sure we can understand their level of skills, knowledge, and experience.

## Goals
Why does this person use the application? What are they trying to achieve?

## Needs
What does this person need from the application? What are their pain points?

```

### Relevant issue
>
> Link to an issue that covers a pain point relevant to the persona and explain why it is relevant.
>
### Validation

You will validate your design by meeting with a user representative: the product owner (in this case, your lecturer).

##### Meeting minutes

- [x] Meeting held on [date] 11/06/2024
- [x] Persona discussed: [persona name] All of them
- [x] Design artefacts reviewed: [list of artifacts]
- [x] Issues discussed: [list of issues]
- [x] Feedback provided: [feedback]

##### What worked well

- [ ] [list of things that worked well]
- different personas
- exploring different models
- watching youtube videos of blind users

##### What could be improved

- [ ] [list of things that could be improved]
- validating interaction models (trying it ourselves)
- start easy
- more thoughts on designing
-

##### What will you change before the next meeting

- [ ] [list of things that will be changed before the next meeting] (no commitment to complete this)
- create a wireframe for getting to the nearest capture.
- what if the next capture is 5 minutes away?
- what if the file isn't fully processed yet?
- examine how Ollama can be packed and shipped

##### Were there any questions that needed to be discussed with the user

- [ ] [list of questions that need to be discussed with the user]
- whether the user wants to start with the code, or with the video.

#### Lecturer's checklist (to be used by the lecturer)

- [ ] Persona is well defined
- [ ] Persona is relevant to the application
- [ ] Design artifacts are present and easy to follow
- [ ] Design decisions are based on user needs and goals
- [ ] Appropriate considerations of interaction patterns appropriate for the user
- [ ] Efforts towards realizing at least one significant issue involving user interaction
- [ ] Whole team engagement in the design process
Binary file added design/img/Brainstorming_1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added design/img/Brainstorming_2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added design/img/Sketch_1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added design/img/Sketch_2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 4fde641

Please sign in to comment.