示例#1
0
        public async Task <CourseExportResults> UpdateCourse(Course course, CourseUpdateOptions updateOptions)
        {
            var results = new CourseExportResults(course.Id, updateOptions.StepikCourseId);

            try
            {
                await TryUpdateCourse(course, updateOptions, results).ConfigureAwait(false);
            }
            catch (Exception e)
            {
                results.Exception = e;
            }
            return(results);
        }
示例#2
0
        public async Task <CourseExportResults> InitialExportCourse(Course course, CourseInitialExportOptions exportOptions)
        {
            var results = new CourseExportResults(course.Id, exportOptions.StepikCourseId);

            try
            {
                await TryInitialExportCourse(course, exportOptions, results);
            }
            catch (Exception e)
            {
                results.Error(e.Message);
                results.Exception = e;
            }
            return(results);
        }
示例#3
0
        private async Task <StepikApiBlock> ConvertUlearnNonTextBlockIntoStepikStepBlock(SlideBlock block, StepikApiBlock lastTextBlock, string courseId, Slide slide, int lessonId, CourseExportOptions options, CourseExportResults results)
        {
            switch (block)
            {
            case AbstractExerciseBlock exerciseBlock:
                return(new StepikApiBlock
                {
                    Name = "external-grader",
                    Text = lastTextBlock.Text,
                    Cost = ((ExerciseSlide)slide).Scoring.PassedTestsScore,
                    Source = new StepikApiExternalGraderBlockSource(courseId, slide.Id, options.XQueueName, exerciseBlock.ExerciseInitialCode, stepikCSharpLanguageName)
                });

            case ChoiceBlock choiceBlock:
                return(new StepikApiBlock
                {
                    Name = "choice",
                    Text = lastTextBlock.Text + $"<p><br/><b>{choiceBlock.Text.EscapeHtml()}</b></p>",
                    Cost = choiceBlock.MaxScore,
                    Source = new StepikApiChoiceBlockSource(choiceBlock.Items.Select(ConvertUlearnChoiceItemIntoStepikChoiceOption))
                    {
                        IsMultipleChoice = choiceBlock.Multiple,
                        PreserveOrder = !choiceBlock.Shuffle
                    }
                });

            case YoutubeBlock videoBlock:
            {
                var rawVideoUrl = await youtubeVideoUrlExtractor.GetVideoUrl(videoBlock.GetYoutubeUrl()).ConfigureAwait(false);

                var video = await client.UploadVideo(rawVideoUrl, lessonId).ConfigureAwait(false);

                await Task.Delay(options.PauseAfterVideoUploaded).ConfigureAwait(false);

                return(new StepikApiBlock
                    {
                        Name = "video",
                        Video = video,
                    });
            }

            default:
                results.Error($"Unknown block type for converting into stepik step: {block.GetType().Name}, ignoring it");
                return(null);
            }
        }
示例#4
0
        /* Convert ulearn's blocks and group some of them into on stepik's text block*/
        private async Task <IEnumerable <StepikApiBlock> > ConvertUlearnBlocksIntoStepikBlocks(string courseId, Slide slide, IEnumerable <SlideBlock> blocks, CourseExportOptions options, int stepikLessonId, CourseExportResults results)
        {
            var stepikBlocks = new List <StepikApiBlock>();

            var previousTextBlock = new StepikApiBlock
            {
                Name = "text",
                Text = "",
            };
            var attachTextBlocksToInteractiveContent = slide is QuizSlide || slide is ExerciseSlide;

            foreach (var block in blocks)
            {
                if (block.Hide)
                {
                    continue;
                }

                var isCurrentBlockText = IsCurrentBlockText(block, options);
                if (isCurrentBlockText)
                {
                    previousTextBlock.Text += GetTextForStepikBlockFromUlearnBlock(courseId, slide, block);
                }
                else
                {
                    if (!string.IsNullOrEmpty(previousTextBlock.Text) && !attachTextBlocksToInteractiveContent)
                    {
                        stepikBlocks.Add(previousTextBlock);
                        previousTextBlock = new StepikApiBlock
                        {
                            Name = "text",
                            Text = "",
                        };
                    }

                    var stepikBlock = await ConvertUlearnNonTextBlockIntoStepikStepBlock(
                        block, previousTextBlock, courseId, slide, stepikLessonId, options, results
                        ).ConfigureAwait(false);

                    if (stepikBlock != null)
                    {
                        stepikBlocks.Add(stepikBlock);
                    }
                }
            }
            if (!string.IsNullOrEmpty(previousTextBlock.Text) && !attachTextBlocksToInteractiveContent)
            {
                stepikBlocks.Add(previousTextBlock);
            }

            return(stepikBlocks);
        }
示例#5
0
        private async Task RemoveStepsFromLesson(StepikApiLesson lesson, List <int> removeStepsIds, CourseExportResults results)
        {
            var realStepsIds = lesson.StepsIds;

            var newPosition = 0;

            foreach (var stepId in realStepsIds)
            {
                if (removeStepsIds.Contains(stepId))
                {
                    try
                    {
                        await client.DeleteStep(stepId).ConfigureAwait(false);
                    }
                    catch (StepikApiException e)
                    {
                        results.Error("Can't delete step: " + e.Message);
                    }
                }
                else
                {
                    try
                    {
                        await client.MoveStep(stepId, newPosition + 1).ConfigureAwait(false);

                        // Increment newPosition only if success
                        newPosition++;
                    }
                    catch (StepikApiException e)
                    {
                        results.Error("Can't move step: " + e.Message);
                    }
                }
            }
        }
示例#6
0
        private async Task TryUpdateCourse(Course course, CourseUpdateOptions updateOptions, CourseExportResults results)
        {
            results.Info($"Downloading stepik course #{updateOptions.StepikCourseId}");
            var stepikCourse = await client.GetCourse(updateOptions.StepikCourseId).ConfigureAwait(false);

            results.StepikCourseTitle = stepikCourse.Title;

            results.Info($"Downloading stepik sections for it ({string.Join(", ", stepikCourse.SectionsIds)})");
            var stepikSections = new Dictionary <int, StepikApiSection>();

            // TODO (andgein): download multiple sections in one request
            foreach (var sectionId in stepikCourse.SectionsIds)
            {
                stepikSections[sectionId] = await client.GetSection(sectionId).ConfigureAwait(false);
            }

            var stepikUnitsIds = stepikSections.SelectMany(kvp => kvp.Value.UnitsIds).ToList();

            results.Info($"Downloading stepik units ({string.Join(", ", stepikUnitsIds)}) and lessons for them");
            var stepikUnits = new Dictionary <int, StepikApiUnit>();

            foreach (var unitId in stepikUnitsIds)
            {
                stepikUnits[unitId] = await client.GetUnit(unitId).ConfigureAwait(false);
            }

            var stepikLessonsIds = stepikUnits.Select(kvp => kvp.Value.LessonId);
            var stepikLessons    = new Dictionary <int, StepikApiLesson>();

            foreach (var lessonId in stepikLessonsIds)
            {
                stepikLessons[lessonId] = await client.GetLesson(lessonId).ConfigureAwait(false);
            }

            var sectionIndex = stepikCourse.SectionsIds.Count;

            foreach (var slideUpdateOptions in updateOptions.SlidesUpdateOptions)
            {
                var slideId = slideUpdateOptions.SlideId;
                var slide   = course.FindSlideById(slideId);
                if (slide == null)
                {
                    results.Error($"Unable to find slide {slideId}, continue without it");
                    continue;
                }
                results.Info($"Updating slide «{slide.Title}» with id {slide.Id}");
                var             stepId = slideUpdateOptions.InsertAfterStep;
                StepikApiLesson lesson;
                int             position;

                if (stepId != -1 && stepikLessons.Values.Any(l => l.StepsIds.Contains(stepId)))
                {
                    var lessonId = stepikLessons.FirstOrDefault(kvp => kvp.Value.StepsIds.Contains(stepId)).Key;
                    // Re-download lesson because it can be changed for a while
                    lesson = await client.GetLesson(lessonId).ConfigureAwait(false);

                    position = lesson.StepsIds.FindIndex(stepId);

                    results.Info($"Removing old steps created for this slide: {string.Join(", ", slideUpdateOptions.RemoveStepsIds)}");
                    await RemoveStepsFromLesson(lesson, slideUpdateOptions.RemoveStepsIds, results).ConfigureAwait(false);
                }
                else
                {
                    results.Info("Creating new stepik lesson for this slide");
                    lesson = await CreateLessonAndSectionForSlide(slide, stepikCourse, ++sectionIndex).ConfigureAwait(false);

                    position = 0;
                }

                results.Info($"Inserting steps for slide «{slide.Title}» into lesson {lesson.Id} on position {position}");
                await InsertSlideAsStepsInLesson(course, slide, lesson.Id.Value, position + 1, updateOptions, results);
            }
        }
示例#7
0
        private async Task <int> InsertSlideAsStepsInLesson(Course course, Slide slide, int stepikLessonId, int position, CourseExportOptions options, CourseExportResults results)
        {
            var stepikBlocks        = (await ConvertUlearnBlocksIntoStepikBlocks(course.Id, slide, slide.Blocks, options, stepikLessonId, results).ConfigureAwait(false)).ToList();
            var blocksCountToInsert = stepikBlocks.Count;

            await MoveStepsForSpaceEmpting(stepikLessonId, position, blocksCountToInsert).ConfigureAwait(false);

            foreach (var stepikBlock in stepikBlocks)
            {
                /* Stepik has no step's titles so insert slide title in each step as H1 */
                stepikBlock.Text = $"<h1>{slide.Title.EscapeHtml()}</h1>" + (stepikBlock.Text ?? "");
                var uploadedStep = await client.UploadStep(new StepikApiStepSource
                {
                    Block    = stepikBlock,
                    Position = ++position,
                    LessonId = stepikLessonId,
                    Cost     = stepikBlock.Cost,
                }).ConfigureAwait(false);

                if (!uploadedStep.Id.HasValue)
                {
                    throw new StepikApiException("Didn't receive `step`'s ID from stepik");
                }
                results.SlideIdToStepsIdsMap[slide.Id].Add(uploadedStep.Id.Value);
            }

            return(blocksCountToInsert);
        }
示例#8
0
        private async Task TryInitialExportCourse(Course course, CourseInitialExportOptions exportOptions, CourseExportResults results)
        {
            var stepikCourse = await client.GetCourse(exportOptions.StepikCourseId).ConfigureAwait(false);

            results.StepikCourseTitle = stepikCourse.Title;
            await ClearCourse(stepikCourse).ConfigureAwait(false);

            var unitIndex = 0;

            foreach (var unit in course.Units)
            {
                results.Info($"Converting ulearn unit «{unit.Title}» into stepik section");
                var section = ConvertUlearnUnitIntoStepikSection(unit);
                section.CourseId = exportOptions.StepikCourseId;
                section.Position = ++unitIndex;
                section          = await client.UploadSection(section).ConfigureAwait(false);

                if (!section.Id.HasValue)
                {
                    throw new StepikApiException($"Didn't receive `section`'s ID from stepik: {section.JsonSerialize()}");
                }

                var currentStepikLessonId = -1;
                var blockIndex            = 0;
                var lessonIndex           = 0;
                var isFirstSlideInUnit    = true;
                foreach (var slide in unit.Slides)
                {
                    results.Info($"Converting ulearn slide «{slide.Title}» with id {slide.Id} into stepik steps");
                    var needToStartNewLesson = isFirstSlideInUnit || exportOptions.SlideIdsWhereNewLessonsStart.Contains(slide.Id);
                    isFirstSlideInUnit = false;
                    if (needToStartNewLesson)
                    {
                        results.Info("Starting new stepik lesson for this slide");
                        var lesson = await client.UploadLesson(new StepikApiLesson
                        {
                            Title = slide.Title
                        }).ConfigureAwait(false);

                        if (!lesson.Id.HasValue)
                        {
                            throw new StepikApiException($"Didn't receive `lesson`'s ID from stepik: {lesson.JsonSerialize()}");
                        }

                        currentStepikLessonId = lesson.Id.Value;

                        await client.UploadUnit(new StepikApiUnit
                        {
                            Position  = ++lessonIndex,
                            SectionId = section.Id.Value,
                            LessonId  = currentStepikLessonId,
                        }).ConfigureAwait(false);

                        blockIndex = 0;
                    }

                    blockIndex += await InsertSlideAsStepsInLesson(course, slide, currentStepikLessonId, blockIndex, exportOptions, results).ConfigureAwait(false);
                }
            }
        }