123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570 |
- using PuppeteerSharp;
- namespace StationProbe
- {
- internal enum ElementType
- {
- Page,
- Frame
- }
- internal static class ElementConverter
- {
- public static T? As<T>(this object obj) where T : class
- {
- return obj as T;
- }
- }
- internal class ElementCollection
- {
- private readonly Dictionary<ElementType, object> _objects = new();
- public object this[ElementType type] => _objects[type];
- public void AddObject(ElementType element, object obj)
- {
- _objects.Add(element, obj);
- }
- }
- internal interface IExamStore:IDisposable
- {
- Exam? GetLastExam(int batchTaskId);
- void AddExam(Exam exam);
- void AddImage(Image image);
- void UpdateBatchTask(BatchTask batchTask);
- }
- internal abstract class ProbeTask
- {
- public static bool DebugMode { get; set; }
- private static string _debugFolder = string.Empty;
- protected ScreenshotOptions _snapshotOpts = new ScreenshotOptions() { Type = ScreenshotType.Png };
- protected readonly IExamStore _examStore;
- public ProbeTask(IExamStore examStore)
- {
- _examStore = examStore;
- }
- public static void InitDebugFolder()
- {
- var drives = DriveInfo.GetDrives();
- var largestDriver = drives.OrderByDescending(x => x.TotalFreeSpace).First();
- var debugFolder = largestDriver.Name + "debug";
- if (!Directory.Exists(debugFolder))
- {
- Directory.CreateDirectory(debugFolder);
- }
- var i = 1;
- while (Directory.Exists(Path.Combine(debugFolder, i.ToString("D6"))))
- {
- i++;
- }
- _debugFolder = Path.Combine(debugFolder, i.ToString("D6"));
- Directory.CreateDirectory(_debugFolder);
- }
- public void SaveDebugSnapshot(string name, byte[] imageData)
- {
- if (Directory.Exists(_debugFolder))
- {
- var file = Path.Combine(_debugFolder, name);
- File.WriteAllBytes(file, imageData);
- }
- }
- public abstract Task<ElementCollection> ExecuteAsync(ElementCollection inputs);
- }
- internal class LoginTask : ProbeTask
- {
- private readonly string _loginUrl;
- public LoginTask(IExamStore examStore, string loginUrl) : base(examStore)
- {
- _loginUrl = loginUrl;
- }
- public override async Task<ElementCollection> ExecuteAsync(ElementCollection inputs)
- {
- var page = inputs[ElementType.Page].As<IPage>();
- if (page != null)
- {
- await page.GoToAsync(_loginUrl);
- if (DebugMode)
- {
- var imageData = await page.ScreenshotDataAsync(_snapshotOpts);
- var fileName = "100000000.jpg";
- SaveDebugSnapshot(fileName, imageData);
- }
- //username
- await using (var userElement = await page.WaitForSelectorAsync("input[id=\"userName\"]"))
- {
- await userElement.FocusAsync();
- await page.Keyboard.TypeAsync("justin.xing");
- }
- if (DebugMode)
- {
- var imageData = await page.ScreenshotDataAsync(_snapshotOpts);
- var fileName = "200000000.jpg";
- SaveDebugSnapshot(fileName, imageData);
- }
- //password
- await using (var passwordElement = await page.WaitForSelectorAsync("input[id=\"password\"]"))
- {
- await passwordElement.FocusAsync();
- await page.Keyboard.TypeAsync("vinno@123");
- }
- if (DebugMode)
- {
- var imageData = await page.ScreenshotDataAsync(_snapshotOpts);
- var fileName = "300000000.jpg";
- SaveDebugSnapshot(fileName, imageData);
- }
- //login
- await using (var loginButtonElement = await page.WaitForSelectorAsync("button[b-kvd270uhz9]"))
- {
- await loginButtonElement.ClickAsync();
- await page.WaitForNetworkIdleAsync();
- }
- if (DebugMode)
- {
- var imageData = await page.ScreenshotDataAsync(_snapshotOpts);
- var fileName = "400000000.jpg";
- SaveDebugSnapshot(fileName, imageData);
- }
- }
- return inputs;
- }
- }
- internal class MenuTask : ProbeTask
- {
- public MenuTask(IExamStore examStore) : base(examStore)
- {
- }
- public override async Task<ElementCollection> ExecuteAsync(ElementCollection inputs)
- {
- Logger.WriteLine("Finding menu...");
- var page = inputs[ElementType.Page].As<IPage>();
- if (page != null)
- {
- await using (var menuElement = await page.WaitForSelectorAsync("a[href=\"feedback/list\"]"))
- {
- await menuElement.ClickAsync();
- if (DebugMode)
- {
- var imageData = await page.ScreenshotDataAsync(_snapshotOpts);
- var fileName = "500000000.jpg";
- SaveDebugSnapshot(fileName, imageData);
- }
- }
- }
- return inputs;
- }
- }
- internal class FilterTask : ProbeTask
- {
- private readonly DateTime _start;
- private readonly DateTime _end;
- public int PageCount { get; private set; }
- public int ExamCount { get; private set; }
- public FilterTask(IExamStore examStore, DateTime start, DateTime end) : base(examStore)
- {
- _start = start;
- _end = end;
- }
- public override async Task<ElementCollection> ExecuteAsync(ElementCollection inputs)
- {
- var page = inputs[ElementType.Page].As<IPage>();
- //var frame = inputs[ElementType.Frame].As<IFrame>();
- if (page != null) //&& frame != null)
- {
- //var filterElements = await page.QuerySelectorAllAsync("div[class=\"me-3 my-1\"]");
- //var filterElement1 = filterElements[3];
- //var filterElement2 = filterElements[4];
- ////show filter dialog
- //if (DebugMode)
- //{
- // var imageData = await page.ScreenshotDataAsync(_snapshotOpts);
- // var fileName = "600000000.jpg";
- // SaveDebugSnapshot(fileName, imageData);
- //}
- ////input start
- //var openElement1 = await filterElement1.WaitForSelectorAsync("div[class=\"multi-select\"]");
- //await openElement1.ClickAsync();
- //var optionListElement1 = await filterElement1.WaitForSelectorAsync("div[class=\"dropdown-menu shadow\"]");
- //var optionElements = await optionListElement1.QuerySelectorAllAsync("div[class=\"dropdown-item\"]");
- //var targetElement = optionElements[0];
- //await targetElement.ClickAsync();
- //if (DebugMode)
- //{
- // var imageData = await page.ScreenshotDataAsync(_snapshotOpts);
- // var fileName = "700000000.jpg";
- // SaveDebugSnapshot(fileName, imageData);
- //}
- //var openlement2 = await filterElement2.WaitForSelectorAsync("div[class=\"multi-select\"]");
- //await openlement2.ClickAsync();
- //var optionListElement2 = await filterElement2.WaitForSelectorAsync("div[class=\"dropdown-menu shadow\"]");
- //optionElements = await optionListElement2.QuerySelectorAllAsync("div[class=\"dropdown-item\"]");
- //targetElement = optionElements[0];
- //await targetElement.ClickAsync();
- ////input end
- //if (DebugMode)
- //{
- // var imageData = await page.ScreenshotDataAsync(_snapshotOpts);
- // var fileName = "800000000.jpg";
- // SaveDebugSnapshot(fileName, imageData);
- //}
- //var searchTableElement = await page.QuerySelectorAsync("div[class=\"table-search\"]");
- //var searchButtonElement = await searchTableElement.QuerySelectorAsync("button[class=\"btn btn-primary btn-xs ms-2\"]");
- //await searchButtonElement.ClickAsync();
- //await page.WaitForNetworkIdleAsync();
- //if (DebugMode)
- //{
- // var imageData = await page.ScreenshotDataAsync(_snapshotOpts);
- // var fileName = "900000000.jpg";
- // SaveDebugSnapshot(fileName, imageData);
- //}
- await using (var pageinationElement = await page.QuerySelectorAsync("div[class=\"table-pagination\"]"))
- {
- await using (var examCountElement = await pageinationElement.QuerySelectorAsync("div[class=\"d-none d-sm-inline-block\"]"))
- {
- var examCountStr = await page.EvaluateFunctionAsync<string>("(element) => element.textContent", examCountElement);
- var words = examCountStr.Split(' ');
- ExamCount = int.Parse(words[1]);
- }
- }
- await using (var pagesElement = await page.QuerySelectorAsync("ul[class=\"pagination\"]"))
- {
- var pageItemElements = await pagesElement.QuerySelectorAllAsync("li");
- var lastPageElement = pageItemElements[pageItemElements.Length - 2];
- await using (var textElement = await lastPageElement.QuerySelectorAsync("a"))
- {
- var lastPageStr = await page.EvaluateFunctionAsync<string>("(element) => element.textContent", textElement);
- PageCount = int.Parse(lastPageStr);
- }
- foreach (var pageItemElement in pageItemElements)
- {
- await pageItemElement.DisposeAsync();
- }
- }
- }
- return inputs;
- }
- }
- internal class PageTask : ProbeTask
- {
- private readonly int _page;
- public PageTask(IExamStore examStore, int page) : base(examStore)
- {
- _page = page;
- }
- private async Task JumpToPageAsync(IPage page)
- {
- while (true)
- {
- await using (var pagesElement = await page.WaitForSelectorAsync("ul[class=\"pagination\"]"))
- {
- var pageItemElements = await pagesElement.QuerySelectorAllAsync("li");
- await using (var currentPageElemment = await pagesElement.QuerySelectorAsync("li[class=\"page-item active\"]"))
- {
- await using (var textElement = await currentPageElemment.QuerySelectorAsync("a"))
- {
- var currentPageStr = await page.EvaluateFunctionAsync<string>("(element) => element.textContent", textElement);
- var currentPage = int.Parse(currentPageStr);
- if (currentPage != _page + 1)
- {
- var nextPageElement = pageItemElements[pageItemElements.Length - 1];
- await nextPageElement.ClickAsync();
- foreach(var pageItemElement in pageItemElements)
- {
- await pageItemElement.DisposeAsync();
- }
- }
- else
- {
- foreach (var pageItemElement in pageItemElements)
- {
- await pageItemElement.DisposeAsync();
- }
- break;
- }
- }
- }
- }
- await Task.Delay(DelayConfig.JumpDelay);
- }
- }
- public override async Task<ElementCollection> ExecuteAsync(ElementCollection inputs)
- {
- var page = inputs[ElementType.Page].As<IPage>();
- //var frame = inputs[ElementType.Frame].As<IFrame>();
- if (page != null) //&& frame != null)
- {
- await JumpToPageAsync(page);
- if (DebugMode)
- {
- var imageData = await page.ScreenshotDataAsync(_snapshotOpts);
- var fileName = "9" + _page.ToString("D5") + "000.jpg";
- SaveDebugSnapshot(fileName, imageData);
- }
- }
- return inputs;
- }
- }
- internal class ExamTask : ProbeTask
- {
- private readonly int _batchTaskId;
- private readonly int _pageIndex;
- private readonly int _examIndex;
- private readonly int _examIndexInPage;
- public ExamTask(IExamStore examStore, int batchTaskId, int pageIndex, int examIndex, int examIndexInPage) : base(examStore)
- {
- _batchTaskId = batchTaskId;
- _pageIndex = pageIndex;
- _examIndex = examIndex;
- _examIndexInPage = examIndexInPage;
- }
- private async Task JumpToPageAsync(IPage page)
- {
- while (true)
- {
- await using (var pagesElement = await page.WaitForSelectorAsync("ul[class=\"pagination\"]"))
- {
- var pageItemElements = await pagesElement.QuerySelectorAllAsync("li");
- await using (var currentPageElemment = await pagesElement.QuerySelectorAsync("li[class=\"page-item active\"]"))
- {
- await using (var textElement = await currentPageElemment.QuerySelectorAsync("a"))
- {
- var currentPageStr = await page.EvaluateFunctionAsync<string>("(element) => element.textContent", textElement);
- var currentPage = int.Parse(currentPageStr);
- if (currentPage != _pageIndex + 1)
- {
- var nextPageElement = pageItemElements[pageItemElements.Length - 1];
- await nextPageElement.ClickAsync();
- foreach (var pageItemElement in pageItemElements)
- {
- await pageItemElement.DisposeAsync();
- }
- }
- else
- {
- foreach (var pageItemElement in pageItemElements)
- {
- await pageItemElement.DisposeAsync();
- }
- break;
- }
- }
- }
- }
- await Task.Delay(DelayConfig.JumpDelay);
- }
- }
- private async Task EnterExamAsync(IPage page)
- {
- await JumpToPageAsync(page);
- await using (var tableElement = await page.QuerySelectorAsync("table[class=\"table table-bordered table-striped table-hover\"]"))
- {
- var examLinkElements = await tableElement.QuerySelectorAllAsync("div[style=\"cursor:pointer;\"]");
- var eaxmLinkElement = examLinkElements[_examIndexInPage];
- await eaxmLinkElement.ClickAsync();
- foreach(var examLinkElement in examLinkElements)
- {
- await examLinkElement.DisposeAsync();
- }
- }
- }
- private async Task ExitExamAsync(IPage page)
- {
- //Wait for the back button.
- await using (var backButtonElement = await page.WaitForSelectorAsync("button[class=\"btn fix-item feedback-common-button\"]"))
- {
- await page.EvaluateFunctionAsync("b => b.click()", backButtonElement);
- }
- }
- private async void DoSaveExamImageAsync(IPage page, string examId, int imageIndex, string imageUrl, ManualResetEvent finishEvent)
- {
- var response = await page.WaitForResponseAsync(imageUrl);
- var imageData = await response.BufferAsync();
- var image = new Image()
- {
- Id = Guid.NewGuid().ToString("N"),
- ExamId = examId,
- ImageIndex = imageIndex,
- Data = imageData,
- CreateTime = DateTime.Now,
- };
- _examStore.AddImage(image);
- Logger.WriteLine($"Image:{imageUrl} stored.");
- finishEvent.Set();
- }
- private async Task SaveExamImageAsync(IPage page, string examId, int imageIndex, string imageUrl)
- {
- await using (var imagePage = await page.Browser.NewPageAsync())
- {
- var finishEvent = new ManualResetEvent(false);
- finishEvent.Reset();
- DoSaveExamImageAsync(imagePage, examId, imageIndex, imageUrl, finishEvent);
- await imagePage.GoToAsync(imageUrl);
- finishEvent.WaitOne();
- finishEvent.Dispose();
- }
- }
- private async Task<List<string>> GetImageUrlsAsync(IPage page)
- {
- //Get all image urls.
- var imageUrls = new List<string>();
- await using (var backButtonElement = await page.WaitForSelectorAsync("button[class=\"btn fix-item feedback-common-button\"]"))
- {
- var imageDivElements = await page.QuerySelectorAllAsync("div[class=\"bb-img is-preview\"]");
- if (imageDivElements != null)
- {
- foreach (var imageDivElement in imageDivElements)
- {
- await using (var imageElement = await imageDivElement.QuerySelectorAsync("img"))
- {
- await using (var imageUrl = await imageElement.GetPropertyAsync("src"))
- {
- var url = await imageUrl.JsonValueAsync<string>();
- imageUrls.Add(url);
- }
- }
- await imageDivElement.DisposeAsync();
- }
- }
- }
- Logger.WriteLine($"Get image url count:{imageUrls.Count}");
- return imageUrls;
- }
- private async Task<string> GetReportAsync(IPage page)
- {
- var report = "";
- var reportElements = await page.QuerySelectorAllAsync("div[class=\"col\"]");
- foreach (var reportElement in reportElements)
- {
- await using (var strong = await reportElement.QuerySelectorAsync("strong"))
- {
- if (strong != null)
- {
- var content = await page.EvaluateFunctionAsync<string>("(element) => element.textContent", strong);
- report += " " + content;
- }
- }
- await reportElement.DisposeAsync();
- }
- Logger.WriteLine($"Get report:{report}");
- return report;
- }
- public override async Task<ElementCollection> ExecuteAsync(ElementCollection inputs)
- {
- var page = inputs[ElementType.Page].As<IPage>();
- //var frame = inputs[ElementType.Frame].As<IFrame>();
- if (page != null) //&& frame != null)
- {
- var examId = Guid.NewGuid().ToString("N");
- //First time enter exam.
- await EnterExamAsync(page);
- //wait for page loaded.
- await Task.Delay(DelayConfig.ExamContentDelay);
-
- var imageUrls = await GetImageUrlsAsync(page);
- for (var i = 0; i < imageUrls.Count; i++)
- {
- await SaveExamImageAsync(page, examId, i, imageUrls[i]);
- //Do not save to fast to avoid network trouble.
- await Task.Delay(DelayConfig.SaveImageDelay);
- }
- if (DebugMode)
- {
- var imageData = await page.ScreenshotDataAsync(_snapshotOpts);
- var fileName = "9" + _pageIndex.ToString("D5") + _examIndexInPage.ToString("D3") + ".jpg";
- SaveDebugSnapshot(fileName, imageData);
- }
- //Get patient info
- //var patientName = "";
- var patientSex = "";
- var patientAge = 0;
- //var examDate = DateTime.Now;
- var report = await GetReportAsync(page);
- //Save exam.
- var exam = new Exam()
- {
- Id = examId,
- PatientName = "",
- PatientSex = patientSex,
- PatientAge = patientAge,
- ExamDate = DateTime.Now,
- Report = report,
- BatchTaskId = _batchTaskId,
- ExamIndex = _examIndex,
- PageIndex = _pageIndex,
- ExamIndexInPage = _examIndexInPage,
- CreateTime = DateTime.Now
- };
- _examStore.AddExam(exam);
- //Back to examl list page.
- await ExitExamAsync(page);
- if (DebugMode)
- {
- var imageData = await page.ScreenshotDataAsync(_snapshotOpts);
- var fileName = "9" + _pageIndex.ToString("D5") + "999.jpg";
- SaveDebugSnapshot(fileName, imageData);
- }
- }
- return inputs;
- }
- }
- }
|