Skip to content

Commit

Permalink
💻 Programs count (#5278)
Browse files Browse the repository at this point in the history
Fixes #5193 
Fixes #5192 
Fixes #1275

This fixes that only the programs which are modified are counted as (for now) programs saved.
[This](#5386) is the new issue for the design that shows this counter (where programs_saved will be changed):


**Also solved that only if a program is modified, the confetti button is shown:**

https://github.com/hedyorg/hedy/assets/48122190/dd0e6fe2-5484-4192-8f92-be01d1d5607e


How to test:
- Run a program with preview code, make sure it doesn't get added to my-programs
- Run a program with 'modified code' so not the preview code and make sure it does get added to my programs
- Remove a program from my programs and make sure the counter and the options in the forms get properly updated
  • Loading branch information
Annelein authored Apr 23, 2024
1 parent 05adb9c commit 1e9ef65
Show file tree
Hide file tree
Showing 12 changed files with 231 additions and 68 deletions.
50 changes: 33 additions & 17 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ def get_locale():
DATABASE = database.Database()
ACHIEVEMENTS = achievements.Achievements(DATABASE, ACHIEVEMENTS_TRANSLATIONS)
SURVEYS = surveys.SurveysModule(DATABASE)
STATISTICS = statistics.StatisticsModule(DATABASE)

TAGS = collections.defaultdict(hedy_content.NoSuchAdventure)
for lang in ALL_LANGUAGES.keys():
Expand Down Expand Up @@ -658,17 +659,20 @@ def parse():
# Save this program (if the user is logged in)
if username and body.get('save_name'):
try:
program_logic = programs.ProgramsLogic(DATABASE, ACHIEVEMENTS)
program_logic = programs.ProgramsLogic(DATABASE, ACHIEVEMENTS, STATISTICS)
program = program_logic.store_user_program(
user=current_user(),
level=level,
name=body.get('save_name'),
program_id=body.get('program_id'),
adventure_name=body.get('adventure_name'),
short_name=body.get('short_name'),
code=code,
error=exception is not None)

response['save_info'] = SaveInfo.from_program(Program.from_database_row(program))
if program.get('is_modified'):
response['is_modified'] = True
except programs.NotYourProgramError:
# No permissions to overwrite, no biggie
pass
Expand Down Expand Up @@ -1016,6 +1020,8 @@ def programs_page(user):
program['adventure_name'] not in adventure_names:
ids_to_fetch.append(program['adventure_name'])

all_programs = [program for program in all_programs if program.get('is_modified')]

teacher_adventures = DATABASE.batch_get_adventures(ids_to_fetch)
for id, teacher_adventure in teacher_adventures.items():
if teacher_adventure is not None:
Expand All @@ -1033,19 +1039,20 @@ def programs_page(user):
date = utils.delta_timestamp(item['date'])
# This way we only keep the first 4 lines to show as preview to the user
preview_code = "\n".join(item['code'].split("\n")[:4])
programs.append(
{'id': item['id'],
'preview_code': preview_code,
'code': item['code'],
'date': date,
'level': item['level'],
'name': item['name'],
'adventure_name': item.get('adventure_name'),
'submitted': item.get('submitted'),
'public': item.get('public'),
'number_lines': item['code'].count('\n') + 1
}
)
if item.get('is_modified'):
programs.append(
{'id': item['id'],
'preview_code': preview_code,
'code': item['code'],
'date': date,
'level': item['level'],
'name': item['name'],
'adventure_name': item.get('adventure_name'),
'submitted': item.get('submitted'),
'public': item.get('public'),
'number_lines': item['code'].count('\n') + 1
}
)

sorted_level_programs = hedy_content.Adventures(g.lang) \
.get_sorted_level_programs(all_programs, adventure_names)
Expand All @@ -1067,7 +1074,8 @@ def programs_page(user):
adventure_names=adventure_names,
max_level=hedy.HEDY_MAX_LEVEL,
next_page_url=next_page_url,
second_teachers_programs=False)
second_teachers_programs=False,
user_program_count=len(programs))


@app.route('/logs/query', methods=['POST'])
Expand Down Expand Up @@ -2658,6 +2666,13 @@ def public_user_page(username):
'public_user_page',
username=username, **dict(request.args,
page=next_page_token)) if next_page_token else None

user = DATABASE.user_by_username(username)
if user.get('program_count'):
user_program_count = user.get('program_count')
else:
user_program_count = 0

return render_template(
'public-page.html',
user_info=user_public_info,
Expand All @@ -2670,7 +2685,8 @@ def public_user_page(username):
certificate_message=certificate_message,
next_page_url=next_page_url,
sorted_level_programs=sorted_level_programs,
sorted_adventure_programs=sorted_adventure_programs
sorted_adventure_programs=sorted_adventure_programs,
user_program_count=user_program_count,
)
return utils.error_page(error=404, ui_message=gettext('user_not_private'))

Expand Down Expand Up @@ -2733,7 +2749,7 @@ def current_user_allowed_to_see_program(program):

app.register_blueprint(auth_pages.AuthModule(DATABASE))
app.register_blueprint(profile.ProfileModule(DATABASE))
app.register_blueprint(programs.ProgramsModule(DATABASE, ACHIEVEMENTS))
app.register_blueprint(programs.ProgramsModule(DATABASE, ACHIEVEMENTS, STATISTICS))
app.register_blueprint(for_teachers.ForTeachersModule(DATABASE, ACHIEVEMENTS))
app.register_blueprint(classes.ClassModule(DATABASE, ACHIEVEMENTS))
app.register_blueprint(classes.MiscClassPages(DATABASE, ACHIEVEMENTS))
Expand Down
70 changes: 63 additions & 7 deletions static/js/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,8 @@ export function initializeHighlightedCodeBlocks(where: Element) {
if (dir === "rtl") {
symbol = "⇤";
}
$('<button>').css({ fontFamily: 'sans-serif' }).addClass('yellow-btn').text(symbol).appendTo(buttonContainer).click(function() {
const adventure = container.getAttribute('data-tabtarget')
$('<button>').css({ fontFamily: 'sans-serif' }).addClass('yellow-btn').attr('data-cy', `paste-example-code-${adventure}`).text(symbol).appendTo(buttonContainer).click(function() {
if (!theGlobalEditor?.isReadOnly) {
theGlobalEditor.contents = exampleEditor.contents + '\n';
}
Expand Down Expand Up @@ -538,6 +539,7 @@ export async function runit(level: number, lang: string, raw: boolean, disabled_
tutorial: $('#code_output').hasClass("z-40"), // if so -> tutorial mode
read_aloud : !!$('#speak_dropdown').val(),
adventure_name: adventureName,
short_name: adventure ? adventure.short_name : undefined,
raw: raw,

// Save under an existing id if this field is set
Expand Down Expand Up @@ -584,7 +586,7 @@ export async function runit(level: number, lang: string, raw: boolean, disabled_
program_data = theGlobalDebugger.get_program_data();
}

runPythonProgram(program_data.Code, program_data.source_map, program_data.has_turtle, program_data.has_pressed, program_data.has_sleep, program_data.has_clear, program_data.has_music, program_data.Warning, cb, run_type).catch(function(err: any) {
runPythonProgram(program_data.Code, program_data.source_map, program_data.has_turtle, program_data.has_pressed, program_data.has_sleep, program_data.has_clear, program_data.has_music, program_data.Warning, program_data.is_modified ,cb, run_type).catch(function(err: any) {
// The err is null if we don't understand it -> don't show anything
if (err != null) {
error.show(ClientMessages['Execute_error'], err.message);
Expand Down Expand Up @@ -720,12 +722,65 @@ export function viewProgramLink(programId: string) {
return window.location.origin + '/hedy/' + programId + '/view';
}

function updateProgramCount() {
const programCountDiv = $('#program_count');
const countText = programCountDiv.text();
const regex = /(\d+)/;
const match = countText.match(regex);

if (match && match.length > 0) {
const currentCount = parseInt(match[0]);
const newCount = currentCount - 1;
const newText = countText.replace(regex, newCount.toString());
programCountDiv.text(newText);
}
}

function updateSelectOptions(selectName: string) {
let optionsArray: string[] = [];
const select = $(`select[name='${selectName}']`);

// grabs all the levels and names from the remaining adventures
$(`[id="program_${selectName}"]`).each(function() {
const text = $(this).text().trim();
if (selectName == 'level'){
const number = text.match(/\d+/)
if (number && !optionsArray.includes(number[0])) {
optionsArray.push(number[0]);
}
} else if (!optionsArray.includes(text)){
optionsArray.push(text);
}
console.log(optionsArray);
});

if (selectName == 'level'){
optionsArray.sort();
}
// grabs the -- level -- or -- adventure -- from the options
const firstOption = select.find('option:first').text().trim();
optionsArray.unshift(firstOption);

select.empty();
optionsArray.forEach(optionText => {
const option = $('<option></option>').text(optionText);
select.append(option);
});
}

export async function delete_program(id: string, prompt: string) {
await modal.confirmP(prompt);
await tryCatchPopup(async () => {
$('#program_' + id).remove();
// only shows the remaining levels and programs in the options
updateSelectOptions('level');
updateSelectOptions('adventure');
// this function decreases the total programs saved
updateProgramCount();
const response = await postJsonWithAchievements('/programs/delete', { id });
showAchievements(response.achievement, true, "");
$('#program_' + id).remove();
// issue request on the Bar component.
console.log("resp", response)
modal.notifySuccess(response.message);
});
}
Expand Down Expand Up @@ -853,7 +908,7 @@ window.onerror = function reportClientException(message, source, line_number, co
});
}

export function runPythonProgram(this: any, code: string, sourceMap: any, hasTurtle: boolean, hasPressed: boolean, hasSleep: number[], hasClear: boolean, hasMusic: boolean, hasWarnings: boolean, cb: () => void, run_type: "run" | "debug" | "continue") {
export function runPythonProgram(this: any, code: string, sourceMap: any, hasTurtle: boolean, hasPressed: boolean, hasSleep: number[], hasClear: boolean, hasMusic: boolean, hasWarnings: boolean, isModified: boolean, cb: () => void, run_type: "run" | "debug" | "continue") {
// If we are in the Parsons problem -> use a different output
let outputDiv = $('#output');
let skip_faulty_found_errors = false;
Expand Down Expand Up @@ -1048,7 +1103,7 @@ export function runPythonProgram(this: any, code: string, sourceMap: any, hasTur
return;
}
if (!hasWarnings && code !== last_code) {
showSuccesMessage(); //FH nov 2023: typo in success :)
showSuccessMessage(isModified);
last_code = code;
}
if (cb) cb ();
Expand Down Expand Up @@ -1430,11 +1485,11 @@ export function modalStepOne(level: number){
}
}

function showSuccesMessage(){
function showSuccessMessage(isModified: boolean){
removeBulb();
var allsuccessmessages = ClientMessages['Transpile_success'].split('\n');
var randomnum: number = Math.floor(Math.random() * allsuccessmessages.length);
success.show(allsuccessmessages[randomnum]);
success.show(allsuccessmessages[randomnum], isModified);
}

function createModal(level:number ){
Expand Down Expand Up @@ -1970,6 +2025,7 @@ async function saveIfNecessary() {
program_id: saveInfo?.id,
// We pass 'public' in here to save the backend a lookup
share: saveInfo?.public,
short_name: adventure.short_name,
});

// Record that we saved successfully
Expand Down
66 changes: 57 additions & 9 deletions static/js/appbundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -56937,7 +56937,10 @@ ${i2}` : o2;
$("#okbox .details").text(message);
$("#okbox").show();
},
show(caption) {
show(caption, confetti) {
if (confetti) {
$("#confetti-button").show();
}
$("#okbox .caption").text(caption);
$("#okbox").show();
setTimeout(function() {
Expand Down Expand Up @@ -99100,7 +99103,8 @@ def note_with_error(value, err):
if (dir === "rtl") {
symbol = "\u21E4";
}
$("<button>").css({ fontFamily: "sans-serif" }).addClass("yellow-btn").text(symbol).appendTo(buttonContainer).click(function() {
const adventure = container.getAttribute("data-tabtarget");
$("<button>").css({ fontFamily: "sans-serif" }).addClass("yellow-btn").attr("data-cy", `paste-example-code-${adventure}`).text(symbol).appendTo(buttonContainer).click(function() {
if (!(theGlobalEditor == null ? void 0 : theGlobalEditor.isReadOnly)) {
theGlobalEditor.contents = exampleEditor.contents + "\n";
}
Expand Down Expand Up @@ -99207,6 +99211,7 @@ def note_with_error(value, err):
tutorial: $("#code_output").hasClass("z-40"),
read_aloud: !!$("#speak_dropdown").val(),
adventure_name: adventureName,
short_name: adventure ? adventure.short_name : void 0,
raw,
program_id: isServerSaveInfo(adventure == null ? void 0 : adventure.save_info) ? adventure.save_info.id : void 0,
save_name: saveNameFromInput()
Expand Down Expand Up @@ -99242,7 +99247,7 @@ def note_with_error(value, err):
} else {
program_data = theGlobalDebugger.get_program_data();
}
runPythonProgram(program_data.Code, program_data.source_map, program_data.has_turtle, program_data.has_pressed, program_data.has_sleep, program_data.has_clear, program_data.has_music, program_data.Warning, cb2, run_type).catch(function(err) {
runPythonProgram(program_data.Code, program_data.source_map, program_data.has_turtle, program_data.has_pressed, program_data.has_sleep, program_data.has_clear, program_data.has_music, program_data.Warning, program_data.is_modified, cb2, run_type).catch(function(err) {
if (err != null) {
error.show(ClientMessages["Execute_error"], err.message);
reportClientError(level3, code, err.message);
Expand Down Expand Up @@ -99352,12 +99357,54 @@ def note_with_error(value, err):
function viewProgramLink(programId) {
return window.location.origin + "/hedy/" + programId + "/view";
}
function updateProgramCount() {
const programCountDiv = $("#program_count");
const countText = programCountDiv.text();
const regex = /(\d+)/;
const match = countText.match(regex);
if (match && match.length > 0) {
const currentCount = parseInt(match[0]);
const newCount = currentCount - 1;
const newText = countText.replace(regex, newCount.toString());
programCountDiv.text(newText);
}
}
function updateSelectOptions(selectName) {
let optionsArray = [];
const select = $(`select[name='${selectName}']`);
$(`[id="program_${selectName}"]`).each(function() {
const text = $(this).text().trim();
if (selectName == "level") {
const number2 = text.match(/\d+/);
if (number2 && !optionsArray.includes(number2[0])) {
optionsArray.push(number2[0]);
}
} else if (!optionsArray.includes(text)) {
optionsArray.push(text);
}
console.log(optionsArray);
});
if (selectName == "level") {
optionsArray.sort();
}
const firstOption = select.find("option:first").text().trim();
optionsArray.unshift(firstOption);
select.empty();
optionsArray.forEach((optionText) => {
const option2 = $("<option></option>").text(optionText);
select.append(option2);
});
}
async function delete_program(id2, prompt) {
await modal.confirmP(prompt);
await tryCatchPopup(async () => {
$("#program_" + id2).remove();
updateSelectOptions("level");
updateSelectOptions("adventure");
updateProgramCount();
const response = await postJsonWithAchievements("/programs/delete", { id: id2 });
showAchievements(response.achievement, true, "");
$("#program_" + id2).remove();
console.log("resp", response);
modal.notifySuccess(response.message);
});
}
Expand Down Expand Up @@ -99462,7 +99509,7 @@ def note_with_error(value, err):
user_agent: navigator.userAgent
});
};
function runPythonProgram(code, sourceMap, hasTurtle, hasPressed, hasSleep, hasClear, hasMusic, hasWarnings, cb2, run_type) {
function runPythonProgram(code, sourceMap, hasTurtle, hasPressed, hasSleep, hasClear, hasMusic, hasWarnings, isModified, cb2, run_type) {
let outputDiv = $("#output");
let skip_faulty_found_errors = false;
let warning_box_shown = false;
Expand Down Expand Up @@ -99611,7 +99658,7 @@ def note_with_error(value, err):
return;
}
if (!hasWarnings && code !== last_code) {
showSuccesMessage();
showSuccessMessage(isModified);
last_code = code;
}
if (cb2)
Expand Down Expand Up @@ -99897,11 +99944,11 @@ def note_with_error(value, err):
theModalEditor = editorCreator2.initializeEditorWithGutter($modalEditor, EditorType.MODAL, dir);
}
}
function showSuccesMessage() {
function showSuccessMessage(isModified) {
removeBulb();
var allsuccessmessages = ClientMessages["Transpile_success"].split("\n");
var randomnum = Math.floor(Math.random() * allsuccessmessages.length);
success.show(allsuccessmessages[randomnum]);
success.show(allsuccessmessages[randomnum], isModified);
}
function createModal(level3) {
let editor = `<div id='modal-editor' class="w-full flex-1 text-lg rounded" style='height:200px; width:50vw;'></div>`.replace("{level}", level3.toString());
Expand Down Expand Up @@ -100294,7 +100341,8 @@ def note_with_error(value, err):
code,
adventure_name: adventureName,
program_id: saveInfo == null ? void 0 : saveInfo.id,
share: saveInfo == null ? void 0 : saveInfo.public
share: saveInfo == null ? void 0 : saveInfo.public,
short_name: adventure.short_name
});
adventure.editor_contents = code;
if (response.save_info) {
Expand Down
4 changes: 2 additions & 2 deletions static/js/appbundle.js.map

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion static/js/modal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,10 @@ export const success = {
$('#okbox').show();
},

show(caption: string) {
show(caption: string, confetti: boolean) {
if (confetti){
$('#confetti-button').show();
}
$('#okbox .caption').text(caption);
$('#okbox').show();
setTimeout(function() {
Expand Down
Loading

0 comments on commit 1e9ef65

Please sign in to comment.