顯示具有 review 標籤的文章。 顯示所有文章
顯示具有 review 標籤的文章。 顯示所有文章

2025年2月21日 星期五

Python 開發筆記 - promptpack-for-code 一個將專案的目錄結構和指定檔案輸出單一文字檔,讓 AI 點評跟建議的小工具


近半年已經極高的比例都會用 claude.ai 做事,最近用量太大後,再加上 grok.com 免費中,就想到如果碰到付費額度用原被凍時,該怎樣快速換另一家 AI 服務使用,也可以避免與 AI 輔助努力了老半天,重啟對話又要打得落落長的內容。


因此就開發了一個小工具:promptpack-for-code

他的用法就是把指定專案的目錄結構、指定的程式碼輸出成 output.txt ,接著就可以上傳 output.txt 跟 AI 互動,成果包括可以請他點評哪邊可以優化、撰寫系統架構、教學文件等等,十分方便

```
% python3 -m venv venv
% source venv/bin/activate
(venv) /tmp % pip install promptpack-for-code
% git clone https://github.com/changyy/py-promptpack-for-code
% promptpack-for-code py-promptpack-for-code -r py-promptpack-for-code 
Successfully created output.txt
```

問問此時此刻(2025-02-21)最強的 Grok 3 AI:

```
### 專案概述

`py-promptpack-for-code` 是一個 Python 命令列工具,旨在將多個程式碼檔案或目錄中的檔案打包成一個結構化的單一文字檔,特別針對與 AI 語言模型進行程式碼審查或分析的需求進行優化。這個工具的主要功能包括:

1. **檔案結構生成**:根據指定的根目錄生成樹狀目錄結構,方便理解專案的組織。
2. **內容整合**:將指定目錄中的程式碼檔案內容合併到輸出檔案,並保留相對路徑。
3. **過濾與忽略**:支援自訂忽略模式(例如 `.git`、`.pyc` 等),避免不必要的檔案內容。
4. **進度顯示**:提供進度條選項,適合處理大型專案時使用。
5. **易於與 AI 整合**:輸出格式規範且結構化,便於直接輸入到 AI 聊天平台進行分析。

### 專案評論

#### 優點
1. **實用性強**:
   - 該工具解決了一個具體且常見的痛點:如何快速準備程式碼供 AI 分析。它不僅合併檔案,還提供了目錄結構,這對理解上下文非常有幫助。
   - 支援多目錄處理、進度條、強制覆蓋等功能,滿足不同使用場景的需求。

2. **結構清晰**:
   - 專案採用標準 Python 包結構(`src/`、`tests/` 等),易於維護和擴展。
   - 文件(如 `README.md` 和 `LICENSE`)完整,提供了詳細的使用說明和授權資訊。

3. **測試覆蓋良好**:
   - `tests/test_core.py` 包含多個單元測試,涵蓋核心功能(樹狀結構生成、檔案處理、忽略模式等),提高了程式碼的可靠性。

4. **部署與分享便利**:
   - 透過 `setup.py` 和 GitHub Actions(`python-publish.yml`)實現 PyPI 發佈,使用者只需 `pip install` 即可使用。
   - MIT 許可證允許廣泛的開源使用和修改。

5. **效能考慮**:
   - 使用 `ThreadPoolExecutor` 實現並行處理檔案,對於大型專案具有一定的效能優勢。

#### 不足之處
1. **功能單一性**:
   - 目前工具主要專注於生成文字輸出,缺乏更高級的分析或格式化選項(例如 JSON 輸出或程式碼摘要生成)。
   - 對於非文字檔案的處理較為簡單(僅跳過),可能無法滿足需要處理混合內容的需求。

2. **錯誤處理有限**:
   - 雖然有基本的日誌記錄(`logging`),但對於檔案編碼錯誤(`UnicodeDecodeError`)或權限問題的處理僅限於跳過或警告,缺乏更靈活的應對策略。

3. **文件不夠詳盡**:
   - `README.md` 提供了基本用法,但對於進階功能(例如自訂忽略模式的具體範例)或輸出檔案的詳細格式說明不夠深入。
   - 缺少 API 文件,對於希望程式化調用的開發者不夠友好。

4. **忽略模式的靈活性**:
   - 雖然支援 `fnmatch` 通配符,但忽略模式僅限於檔案名稱,無法根據檔案內容或大小進行過濾。

### 優化建議

1. **增加輸出格式選項**:
   - 除了目前的純文字輸出,新增 JSON 或 YAML 格式選項,讓使用者可以更方便地將結果用於其他工具或程式化處理。
   - 例如:
     ```json
     {
       "tree": {
         "project-name": {
           "src": {
             "main.py": "def main():\n    print('Hello')\n",
             "utils": {
               "helper.py": "def helper():\n    return True\n"
             }
           }
         }
       }
     }
     ```

2. **增強錯誤處理與回饋**:
   - 提供選項讓使用者決定如何處理非 UTF-8 檔案(例如轉換編碼或以二進位模式顯示部分內容)。
   - 對於權限錯誤,記錄到輸出檔案中,而不是僅在日誌中警告,這樣使用者能更容易追蹤問題。

3. **擴展忽略模式功能**:
   - 支援基於檔案大小、修改時間或內容關鍵字的過濾。例如:
     ```bash
     promptpack-for-code /path/to/src --ignore-size ">10MB" --ignore-modified "<2023-01-01"
     ```
   - 允許從外部檔案(如 `.gitignore`)載入忽略模式,增強與現有工作流的兼容性。

4. **改善文件與範例**:
   - 在 `README.md` 中新增更多實際範例,例如處理大型專案或與 AI 工具(如 ChatGPT 或 GitHub Copilot)配合的具體流程。
   - 提供一份獨立的 `CONTRIBUTING.md`,詳細說明如何提交問題或貢獻程式碼。

5. **新增摘要功能**:
   - 提供選項生成程式碼摘要(例如每個檔案的前幾行或函數簽名),減少輸出檔案的大小,方便快速預覽。
     ```bash
     promptpack-for-code /path/to/src --summary
     ```

6. **支援增量更新**:
   - 增加一個模式,僅處理自上次運行以來有變更的檔案(例如根據檔案修改時間戳記),提高處理效率。
     ```bash
     promptpack-for-code /path/to/src --incremental
     ```

7. **UI 或互動模式**:
   - 開發一個簡單的 TUI(文字使用者介面)或互動模式,讓使用者在命令列中動態選擇要處理的目錄或檔案。

### 總結

`py-promptpack-for-code` 是一個設計良好且實用的工具,特別適合需要與 AI 進行程式碼分析的開發者。其核心功能已經相當成熟,但在靈活性、錯誤處理和文件方面仍有提升空間。透過上述建議,這個專案可以進一步滿足更廣泛的使用場景,並提升使用者的體驗。

如果你有具體的使用需求或想討論某個優化方向,我可以進一步提供更詳細的建議或程式碼範例!
```

2015年4月21日 星期二

iOS 開發筆記 - 製作 iOS Simulator app 版本送交給 Facebook 進行 review (creating-ios-simulator-build-for-review)

似乎...Facebook SDK 文件沒找到?只好隨意 Google 一些資料

$ cd /path/project
$ xcodebuild -showsdks
OS X SDKs:
OS X 10.9                     -sdk macosx10.9
OS X 10.10                     -sdk macosx10.10

iOS SDKs:
iOS 8.3                       -sdk iphoneos8.3

iOS Simulator SDKs:
Simulator - iOS 8.3           -sdk iphonesimulator8.3

$ xcodebuild -arch i386 -sdk iphonesimulator8.3


若用 CocoaPods (碰到 ld: library not found for -lPods-XXX ) 或 xcworkspace 維護的,需要多一點指令:

$ xcodebuild -arch i386 -sdk iphonesimulator8.3 -workspace YourName.xcworkspace -scheme YourName

接著,想執行看看:

$ ios-sim launch /Users/user/Library/Developer/Xcode/DerivedData/YourName-xxxxxx/Build/Products/Debug-iphonesimulator/YourName.app

其中 ios-sim 可以逛一下這邊:https://github.com/phonegap/ios-sim

2014年9月11日 星期四

Android 開發筆記 - 透過 gsutil 取得 Google Play App Customer Reviews

依照官方文件簡介,對於 Android app 的評論,系統都會儲存在 Google Cloud Storage 裡頭,需要透過 gsutil 取出來使用,在此透過 pip 安裝 gsutil:

$ sudo pip install gsutil

使用前,需要先到 Google Play Developer Console -> Your android app -> 評分與評論 -> 底下最下方找到 ID,如 pubsite_prod_rev_0123456789。

接著,先設定 gsutil 存取權限:

$ gsutil config
This command will create a boto config file at /home/username/.boto
containing your credentials, based on your responses to the following
questions.
Please navigate your browser to the following URL:
https://accounts.google.com/o/oauth2/auth?scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdevstorage.full_control&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&response_type=code&client_id=############.apps.googleusercontent.com&access_type=offline
In your browser you should see a page that requests you to authorize access to Google Cloud Platform APIs and Services on your behalf. After you approve, an authorization code will be displayed.

Enter the authorization code:


並透過瀏覽器得到 authorization code,輸入完後,會在詢問 Project ID,這時請記得填寫類似這串 pubsite_prod_rev_0123456789 的 ID。

之後,就可以透過 gsutil cp -r gs://pubsite_prod_rev_0123456789/reviews/reviews* 取得所有評論。

$ gsutil cp -r gs://pubsite_prod_rev_0123456789/reviews/reviews_* android_review/

這些 reviews 都是 CSV 格式,且第一列有顯示欄位資訊:

Package Name,App Version,Reviewer Language,Reviewer Hardware Model,Review Submit Date and Time,Review Submit Millis Since Epoch,Review Last Update Date and Time,Review Last Update Millis Since Epoch,Star Rating,Review Title,Review Text,Developer Reply Date and Time,Developer Reply Millis Since Epoch,Developer Reply Text,Review Link

看來只要寫隻簡易的 tool 就可以處理完畢囉:

$ file reviews_*.csv
reviews_*.csv: Little-endian UTF-16 Unicode text, with very long lines


因此,寫 php script 的話,可以先用 iconv 轉 UTF-8 後,再搭配 str_getcsv 處理:

<?php
$raw = file_get_contents(...);
$result = iconv($in_charset = 'UTF-16LE' , $out_charset = 'UTF-8' , $raw);
if( false !== $result )
$raw = $result;

$raw_lines = explode("\n", $raw);
array_shift($raw_lines); // title
foreach( $raw_lines as $line ) {
$fields = str_getcsv($line);
print_r($fields);
}

iOS 開發筆記 - 透過 iTunesConnect RSS 處理 App Customer Reviews 資料

以 Facebook 而言,可以輕易得知 App ID 為 284882215,而 iTunesConnect 有提供 RSS 方式查詢 Customer Reviews:

https://itunes.apple.com/us/rss/customerreviews/id=284882215/sortBy=mostRecent/xml

其中,上述有三個主要可變動的參數:country(us), app id (284882215), format(xml)

最近比較愛 json 的:

https://itunes.apple.com/us/rss/customerreviews/id=284882215/sortBy=mostRecent/json

想要台灣區評價:

https://itunes.apple.com/tw/rss/customerreviews/id=284882215/sortBy=mostRecent/json

問題應該就是 country 到底有哪些,可以用既定資料每個都抓一遍啦。

對於格式有興趣的可以用得到可讀性較佳的資料格式:

$ curl https://itunes.apple.com/tw/rss/customerreviews/id=284882215/sortBy=mostRecent/json | python -mjson.tool

若仔細看的話,還可以看到 User ID 、追蹤到 User 對其他款 app 評價等,此外,也有 first page 跟 last page 存取方式:

https://itunes.apple.com/tw/rss/customerreviews/page=1/id=284882215/sortby=mostrecent/json
https://itunes.apple.com/tw/rss/customerreviews/page=10/id=284882215/sortby=mostrecent/json

最大頁數只有到 10 而已(CustomerReviews RSS page depth is limited to 10)

至於要用 json 還是 xml 好?目前的心得是... json 會缺少 user comment updated 資訊,而 xml 卻也會碰到 format error 的情況 Orz 只能...看著辦了 XD


以 PHP 處理為例:

$ cat test.php
<?php
$app_id = '284882215';
$country_list = array( 'tw', 'us');
$format = 'xml';
$page_list = array(1,2,3,4,5,6,7,8,9,10);
foreach( $country_list as $country ) {
        foreach( $page_list as $page ) {
                $url = "https://itunes.apple.com/$country/rss/customerreviews/id=$app_id/page=$page/$format";
                if ($format == 'json') {
                        $raw = json_decode(@file_get_contents($url), true);
                        //print_r($raw);
                        for ($i=1, $cnt=count($raw['feed']['entry']) ; $i<$cnt ; ++$i) {
                                print_r(array(
                                        'user_id' => $raw['feed']['entry'][$i]['id']['label'],
                                        'user_name' => $raw['feed']['entry'][$i]['author']['name']['label'],
                                        'user_uri' => $raw['feed']['entry'][$i]['author']['uri']['label'],
                                        'title' => $raw['feed']['entry'][$i]['title']['label'],
                                        'content' => $raw['feed']['entry'][$i]['content']['label'],
                                        'version' => $raw['feed']['entry'][$i]['im:version']['label'],
                                        'rating' => $raw['feed']['entry'][$i]['im:rating']['label'],
                                ));
                        }
                } else {
                        $raw = simplexml_load_string(@file_get_contents($url));
                        //print_r($raw);
                        for($i=1, $cnt = count($raw->entry); $i<$cnt ; ++$i) {
                                //print_r($raw->entry[$i]);
                                $imAttrs = $raw->entry[$i]->children('im', true);
                                //print_r($imAttrs);
                                print_r(array(
                                        'date' => (string)$raw->entry[$i]->updated,
                                        'user_id' => (string)$raw->entry[$i]->id,
                                        'user_name' => (string)$raw->entry[$i]->author->name,
                                        'user_uri' => (string)$raw->entry[$i]->author->uri,
                                        'title' => (string)$raw->entry[$i]->title,
                                        'content' => (string)$raw->entry[$i]->content[0],
                                        'version' => (string)$imAttrs->version,
                                        'rate' => (string)$imAttrs->rating,
                                ));
                        }
                }
                exit;
        }
}

$ php test.php
Array
(
    [date] => 2014-09-10T09:10:00-07:00
    [user_id] => ###########
    [user_name] => ###########
    [user_uri] => https://itunes.apple.com/tw/reviews/id###########
    [title] => 有Bug請改善
    [content] => 點朋友的動態跑不出來!!
    [version] => 14.0
    [rating] => 1
)
Array
(
    [date] => 2014-09-10T09:03:00-07:00
    [user_id] => ###########
    [user_name] => ###########
    [user_uri] => https://itunes.apple.com/tw/reviews/id###########
    [title] => 一直自動關掉
    [content] => 用一用會一直自動跳掉,很頻繁,很煩。
    [version] => 14.0
    [rating] => 1

)

2014年8月18日 星期一

iOS 開發教學 - 邀請使用者評論、評分 iOS app (Review/Rate your iOS app)

原理就是第一次使用時,埋一個時間進去,等下次使用者使用 iOS app 時,判斷時間是否夠長,達到時間間距時,使用 UIAleterView 詢問使用者是否願意 Rate your app。

假設 iOS app 預設啟用時,停留在某個 ViewController:

@interface ViewController () <UIAlertViewDelegate>
// ...
@end


@implementation ViewController

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    switch (alertView.tag) {
        case YOUR_APP_ID:
        {
            //NSLog(@"buttonIndex: %d", buttonIndex);
            NSDate * now = [[NSDate alloc] init];
            switch (buttonIndex) {
                case 1: // YES
                    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"itms-apps://itunes.apple.com/app/id%d", alertView.tag]]];
                    [[NSUserDefaults standardUserDefaults] setObject:now forKey:@"rateDone"];
                    break;
                case 2: // Remind me later
                    [[NSUserDefaults standardUserDefaults] setObject:now forKey:@"rateDate"];
                    break;
                default:
                    [[NSUserDefaults standardUserDefaults] setObject:now forKey:@"rateDone"];
                    break;
            }
            [[NSUserDefaults standardUserDefaults] synchronize];
        }
            break;
    }
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    @try {
        //[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"rateDone"];
        //[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"rateDate"];
        //[[NSUserDefaults standardUserDefaults] synchronize];
     
        if (![[NSUserDefaults standardUserDefaults] objectForKey:@"rateDone"]) {
            NSDate * now = [[NSDate alloc] init];
            if (![[NSUserDefaults standardUserDefaults] objectForKey:@"rateDate"]) {
                [[NSUserDefaults standardUserDefaults] setObject:now forKey:@"rateDate"];
                [[NSUserDefaults standardUserDefaults] synchronize];
            } else {
                NSDate *prevDate = [[NSUserDefaults standardUserDefaults] objectForKey:@"rateDate"];
                if ([now timeIntervalSinceDate:prevDate] > 60 * 60 * 10) {
                    UIAlertView *alterView = [[UIAlertView alloc] initWithTitle:@"Rate this app" message:@"If you enjoy using this app, would you mind taking a moment to rate it?" delegate:self cancelButtonTitle:@"NO, Thanks" otherButtonTitles:@"YES", @"Remind me later", nil];
                    alterView.tag = YOUR_APP_ID;
                    [alterView show];
                }
            }
        }
    }
    @catch (NSException *exception) {
    }
    @finally {
    }
}

@end

2014年3月5日 星期三

iOS 開發筆記 - App Submission Feedback (Resolution Center) - 2.23: Apps must follow the iOS Data Storage Guidelines or they will be rejected

這理由收過兩次才過,累積成就筆記一下 XD 與其跟 Reviewer 解釋資料產生本來就會耗那麼多空間,不如還是自己乖乖丟到 NSTemporaryDirectory 吧 Orz

We found that your app does not follow the iOS Data Storage Guidelines, which is required per the App Store Review Guidelines.

In particular, we found that on launch and/or content download, your app stores 1x.xx MB. To check how much data your app is storing:

- Install and launch your app
- Go to Settings > iCloud > Storage & Backup > Manage Storage
- If necessary, tap "Show all apps"
- Check your app's storage

The iOS Data Storage Guidelines indicate that only content that the user creates using your app, e.g., documents, new files, edits, etc., should be backed up by iCloud.

Temporary files used by your app should only be stored in the /tmp directory; please remember to delete the files stored in this location when the user exits the app.

Data that can be recreated but must persist for proper functioning of your app - or because customers expect it to be available for offline use - should be marked with the "do not back up" attribute. For NSURL objects, add the NSURLIsExcludedFromBackupKey attribute to prevent the corresponding file from being backed up. For CFURLRef objects, use the corresponding kCFURLIsExcludedFromBackupKey attribute.

For more information, please see Technical Q&A 1719: How do I prevent files from being backed up to iCloud and iTunes?.

It is necessary to revise your app to meet the requirements of the iOS Data Storage Guidelines.
For discrete code-level questions, you may wish to consult with Apple Developer Technical Support. Please be sure to:

- include the complete details of your rejection issues
- prepare any symbolicated crash logs, screenshots, and steps to reproduce the issues for when the DTS engineer follows up.

For information on how to symbolicate and read a crash log, please see Tech Note TN2151 Understanding and Analyzing iPhone OS Application Crash Reports.

If you have difficulty reproducing this issue, please try testing the workflow as described in <https://developer.apple.com/library/ios/qa/qa1764/>Technical Q&A QA1764: How to reproduce a crash or bug that only App Review or users are seeing.


解法:
  • 挑 NSTemporaryDirectory() 來儲存資料
  • 當 app 被強制或使用者手動關閉時,清一下資料吧(連續兩下 Home 等切到背景不算)

    - (void)applicationWillTerminate:(UIApplication *)application {
    // remove data ...
    }