Skip to content
Merged

m #3

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
e09e448
Merge pull request #9 from dimigith/master
jimkon Apr 21, 2023
e71a723
remove html mixin from abstract objects
jimkon Apr 21, 2023
082ba24
docker
jimkon Apr 23, 2023
7058a96
recreate object instead of just build
jimkon Apr 23, 2023
1f2f0d9
plug in is_python_builtin_package
jimkon Apr 23, 2023
e4429f7
PackageRelationsGraphObj modifications
jimkon Apr 24, 2023
e7199e2
css for tables
jimkon Apr 24, 2023
b1f3a31
add html mixin to all objects, and use that when displaying objects i…
jimkon Apr 24, 2023
5d31f16
remove python objects deps from app
jimkon Apr 24, 2023
a75e56e
prettifies class names of objects
jimkon Apr 25, 2023
f98f62e
re-organise object utils
jimkon Apr 25, 2023
e5bdd24
tenp
jimkon Apr 27, 2023
0f8e6ed
rename strategies to adapters
jimkon Apr 30, 2023
8f79da6
json objects
jimkon Apr 30, 2023
7720237
json objects
jimkon Apr 30, 2023
8e2fe9f
text objects
jimkon Apr 30, 2023
094ebb4
tidy up Abstract Object and Singleton
jimkon May 1, 2023
8dbf2d4
tidy up Abstract Object and Singleton
jimkon May 1, 2023
c9d66fb
tidy up Abstract Object and PlantUML objects (HUGE COMMIT)
jimkon May 5, 2023
ef0c4d4
minor
jimkon May 5, 2023
4941459
json2html
jimkon May 5, 2023
eb013e4
json2html
jimkon May 5, 2023
69b00d2
name mixin
jimkon May 6, 2023
a5974a2
move singleton
jimkon May 6, 2023
3a9298c
multi singleton
jimkon May 6, 2023
c9542b2
str key
jimkon May 6, 2023
717ba89
test storage
jimkon May 8, 2023
288b47f
new storage system
jimkon May 13, 2023
811afd4
separate debug page + existing objects menu
jimkon May 13, 2023
43833b2
bug fix
jimkon May 13, 2023
2131519
sort objects in menu
jimkon May 13, 2023
328da01
fix bug [prettified_class_name]
jimkon May 13, 2023
4ba0690
fix bug: if we manually delete files from the file system, storage mo…
jimkon May 13, 2023
1de8ec1
cleaning
jimkon May 13, 2023
8c5f407
object files are stored in different directories based on their file …
jimkon May 13, 2023
bb71155
fix bug: filetype dirs were displaying in the Existing objects menu
jimkon May 13, 2023
7a346b3
todo
jimkon May 13, 2023
be02f8d
minor
jimkon May 13, 2023
1b5fcfb
set separator to none as a default
jimkon May 13, 2023
d86c28a
fix bug: PlantUMLDocumentObj can now return a list
jimkon May 13, 2023
f1ba1c8
global dataset controller in app
jimkon May 13, 2023
56127e3
fix bug: tried to access FullReport.html instead of FullReport
jimkon May 13, 2023
2603d6f
fix bug: tried to access FullReport.html instead of FullReport
jimkon May 13, 2023
f9813bf
fix bug: table titles did not display
jimkon May 13, 2023
c3b34ab
move plantuml document object to data objects
jimkon May 13, 2023
5f31371
fix bug: metrics didn't have a column name
jimkon May 13, 2023
d4e9ea0
feature: rebuild all objects page for easy debugging
jimkon May 13, 2023
e93fc57
UMLClassGraphHTMLObj rename tabs
jimkon May 14, 2023
6822e14
ImportsStatsHTML make it multitab
jimkon May 14, 2023
6e854f5
ModuleRelations make it multitab
jimkon May 14, 2023
0f1fc9e
PackagesImportModuleGraphHTMLObj make it multitab
jimkon May 14, 2023
cfc910d
PackageModuleRelations separate pystruct package, PackageRelations fi…
jimkon May 14, 2023
dd17274
Feature: App: new project from git repo
jimkon May 14, 2023
d57b36c
Feature: App: open project
jimkon May 15, 2023
f9c5084
Feature: App: delete project (permission error on windows)
jimkon May 15, 2023
d13b9ff
attempted to make file browse for loading projects (Failed)
jimkon May 15, 2023
e2f6cf7
bug fix: OSError: [Errno 99] Cannot assign requested address
jimkon May 16, 2023
90b813d
minor
jimkon May 16, 2023
c8571b2
Merge pull request #10 from jimkon/dev
jimkon May 16, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 5 additions & 13 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,21 +1,13 @@
FROM python:3.8.16-slim

#USER admin
#install git first
#WORKDIR /server
RUN apt-get update && apt-get install git -y

RUN python -m pip install --upgrade pip
COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .
COPY . ./app
WORKDIR app
RUN pip install -e .

#CMD [ "python", "-m" , "flask", "run", "--host=0.0.0.0"]

RUN #cd /server
#WORKDIR /server


#CMD [ "flask", "--debug", "run", "--host=0.0.0.0"]
#CMD [ "python", "app.py"]

CMD [ "flask", "--debug", "run", "--host=0.0.0.0"]
157 changes: 106 additions & 51 deletions app.py
Original file line number Diff line number Diff line change
@@ -1,81 +1,136 @@
import abc
import os
import sys

from flask import Flask, render_template, send_file, request, url_for, redirect
import pandas as pd
from flask import Flask, redirect, render_template, request, send_file, url_for, escape

from pystruct.objects.full_report import FullReport
from pystruct.objects.grab_code import grab_code
import pystruct
from pystruct.plat.dataset_controller import DatasetController
from pystruct.utils.object_utils import get_all_concrete_object_classes
from pystruct.utils.object_utils import get_object_class_from_class_name


from pystruct.objects.imports_data_objects import *
from pystruct.objects.metric_tables import *
from pystruct.objects.metric_obj import *
from pystruct.objects.uml_graph_obj import *
dataset_controller = DatasetController.get_instance()
dataset_name = 'No project selected' if dataset_controller.current_dataset is None else dataset_controller.current_dataset.dataset_name

app = Flask(__name__)
app.config['SECRET_KEY'] = 'test_key'


def all_subclasses(cls):
return set(cls.__subclasses__()).union(
[s for c in cls.__subclasses__() for s in all_subclasses(c)])
debug_flag = True
app.logger.info(f"{debug_flag=}")
app.logger.info(f"{dataset_controller.current_dataset=}")


@app.route('/')
def main():
if dataset_controller.current_dataset is None:
return redirect(url_for('project'))

global dataset_name
dataset_name = dataset_controller.current_dataset.name

table_of_content_dict = {k: v.__name__ for k, v in FullReport.content_dict.items()}
debug_flag = app.debug
# all_objects = sorted([_cls.__name__ for _cls in all_subclasses(AbstractObject)
# if (issubclass(_cls, HTMLObject) and not isinstance(_cls, abc.ABC))])
all_objects = sorted([_cls.__name__ for _cls in all_subclasses(AbstractObject)
if (not isinstance(_cls, abc.ABC))])
return render_template('index.html', **locals())
return render_template('index.html', **locals(), **globals())


@app.route('/obj/<obj_class>')
def obj(obj_class):
cls = getattr(sys.modules[__name__], obj_class)
html_object = cls().data()
return render_template('objects.html', **locals())
@app.route('/obj/<obj_class_name>')
def obj(obj_class_name):
print("debug->", dataset_controller.current_dataset.name)
cls = get_object_class_from_class_name(obj_class_name.replace(' ', ''))
html_object = cls().to_html()
return render_template('objects.html', **locals(), **globals())


@app.route('/build_obj/<obj_class>')
def build_obj(obj_class):
cls = getattr(sys.modules[__name__], obj_class)
@app.route('/build_obj/<obj_class_name>')
def build_obj(obj_class_name):
cls = get_object_class_from_class_name(obj_class_name.replace(' ', ''))
cls().delete()
html_object = cls().build()
return render_template('objects.html', **locals())
html_object = cls().to_html()
return render_template('objects.html', **locals(), **globals())


@app.route('/download/<obj_class>')
def download_obj(obj_class):
getattr(sys.modules[__name__], obj_class.split('.')[0])().data() # pre load the obj
filepath = os.path.join(app.root_path, 'report_files/objs/', obj_class)
@app.route('/download/<obj_class_name>')
def download_obj(obj_class_name):
cls = get_object_class_from_class_name(obj_class_name.replace(' ', ''))
cls().data()
filepath = dataset_controller.current_dataset.objects_directory / "html" / f"{obj_class_name}.html"
app.logger.info(f"{filepath}")
return send_file(filepath, as_attachment=True)


@app.route('/load/', methods=['GET', 'POST'])
def load_project():
app.logger.info(f"LOAD_PROJECT> {request.method}")
error_message = ''
source = None
if request.method == 'POST':
if "filepath_input" in request.form:
source = request.form['filepath_input']
elif "giturl_input" in request.form:
source = request.form['giturl_input']
@app.route('/project/', methods=['GET'])
def project():
existing_dataset_names = [dataset.name for dataset in dataset_controller.all_datasets]
return render_template('project_menu.html', **locals(), **globals())


@app.route('/project/open/', methods=['POST'])
def open_project():
dataset_name = request.form['load_project']
dataset_controller.open(dataset_name)
return redirect(url_for('main'))


@app.route('/project/new/source/', methods=['POST'])
def new_source_project():
source = request.form['filepath_input']
dataset_controller.new(dir_path=source)
return redirect(url_for('main'))


@app.route('/project/new/git/', methods=['POST'])
def new_git_project():
git_url = request.form['git_url']
source_directory = request.form['source_directory']
branch = request.form.get('branch', 'master')
dataset_controller.new(git_url=git_url,
code_dir=source_directory,
branch=branch
)
return redirect(url_for('main'))


@app.route('/project/delete/', methods=['POST'])
def delete_project():
dataset_name = request.form['delete_project']
dataset_controller.delete(dataset_name)
return redirect(url_for('main'))


@app.route('/debug')
def debug():
existing_objs = sorted([obj.stem for obj in dataset_controller.current_dataset.objects_directory.rglob('*') if obj.is_file()])
all_objects = sorted([_cls.name() for _cls in get_all_concrete_object_classes()])
return render_template('debug.html', **locals(), **globals())


@app.route('/debug/test_all_objects')
def test_all_objects():
all_objects = [_cls for _cls in get_all_concrete_object_classes()]

results = []
for obj_class in all_objects:
try:
grab_code(source)
return redirect(url_for('main'))
_cls = get_object_class_from_class_name(obj_class.name().replace(' ', ''))
_cls().delete()
result = _cls().build()
except Exception as e:
app.logger.warning(e)
error_message = str(e)
build_result = f"{e.__class__.__name__}: {str(e)}"
else:
build_result = f"Success (len={len(result)})"

return render_template('load_project.html', **locals())
obj_res = {
'object': f"<a href={url_for('build_obj', obj_class_name=obj_class.name())}>{obj_class.name()}</a>",
'build result': build_result
}
results.append(obj_res)

results_df = pd.DataFrame(results).sort_values('build result', ascending=True)
objects_table = results_df.to_html(escape=False)
return render_template('test_all_objects.html', **locals(), **globals())


if __name__ == "__main__":
app.run(host='0.0.0.0', debug=True)

# TODO UMLClassRelationGraphHTMLObj fix and make it multitab
# TODO fix delete dataset functionality (permission denied on windows)
# TODO tidy up the logs
# TODO file explorer for load dataset
# TODO make sure it runs from docker also
3 changes: 2 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ services:
ports:
- "8080:8080"
app:
build: .
build:
dockerfile: ./Dockerfile
ports:
- "5000:5000"
depends_on:
Expand Down
2 changes: 1 addition & 1 deletion pystruct/metrics/import_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def enrich_import_raw_df(df):
df['is_external'] = ~df['is_internal'] & ~df['is_no_imports']

# checks if it is a python built-in module
df['is_builtin'] = ~df['is_internal'] & ~df['is_no_imports']
df['is_builtin'] = df['imports'].apply(is_python_builtin_package)

# finds the module import path for in-project imports
df['import_module'] = df['imports'].apply(lambda x: element_with_longest_match(x, df['module'].unique()))
Expand Down
10 changes: 5 additions & 5 deletions pystruct/metrics/metrics_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import pandas as pd

from pystruct.objects.data_objects import DataframeObject
from pystruct.objects.data_objects import DataframeObjectABC
from pystruct.objects.python_object import PObject
from pystruct.utils.logs import log_yellow
from pystruct.visitors.visitor import TreeNodeVisitor
Expand Down Expand Up @@ -49,11 +49,11 @@ def visit_class_method(self, node):

def results(self):
data_array = list(self._results.items())
return pd.DataFrame.from_records(data_array, columns=['item', self._metric.name])
return pd.DataFrame.from_records(data_array, columns=['item', self._metric.metric_name])


class Metric(abc.ABC):
name = 'no-name'
metric_name = None

def calculate(self, p_obj, **kwargs):
pass
Expand All @@ -74,9 +74,9 @@ def calculate_class_method(self, p_obj, **kwargs):
pass


class MetricObject(DataframeObject, Metric):
class MetricObject(DataframeObjectABC, Metric):
def __init__(self):
super().__init__(read_csv_kwargs={'index_col': None}, to_csv_kwargs={'index': False})
super().__init__()

def build(self):
pobj = PObject().python_source_object()
Expand Down
Loading